Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで配列のすべての部分集合から得られる最大差の合計を効率的に求める方法

問題の概要

要素が重複してもよい n 個の値を持つ配列 A が与えられたとします。このとき、与えられた配列から作れるすべての部分集合について、その部分集合内の最大値と最小値の差(max(s) − min(s))を求め、それらの合計を計算するのが目的です。

ここで、max(s) は部分集合 s 内の最大値、min(s) は最小値を表します。要素が1つだけの部分集合では最大値と最小値が一致するため、差は 0 になります。

具体例

入力が A = [1, 3, 4] の場合を考えてみましょう。

  • [1]、[3]、[4] → 差はそれぞれ 0
  • [1, 3] → 3 − 1 = 2
  • [1, 4] → 4 − 1 = 3
  • [3, 4] → 4 − 3 = 1
  • [1, 3, 4] → 4 − 1 = 3

これらを合計すると 0 + 0 + 0 + 2 + 3 + 1 + 3 = 9 となり、出力は 9 になります。

解法のアプローチ

全部分集合を実際に列挙すると計算量が爆発するため、各要素の「寄与」に着目した効率的な方法を使います。

考え方のポイント

配列を昇順にソートした後、インデックス i の要素 A[i] に注目すると:

  • A[i] が最大値として現れる回数は 2i 回(自分より小さい要素だけで構成される部分集合に自分を加える組み合わせ)
  • A[i] が最小値として現れる回数は 2n−1−i

したがって、答えは次の式で表せます。

答え = Σ A[i] × (2^i − 2^(n−1−i))

大きな指数でもオーバーフローしないよう、剰余 N = 1000000007 を取りながらホーナー法の要領で累積計算していきます。

アルゴリズムの手順

  1. n を配列 A のサイズとする
  2. 配列 A を昇順にソートする
  3. sum_min = 0、sum_max = 0 で初期化する
  4. i を 0 から n−1 まで繰り返し:
    • sum_max = 2 × sum_max + A[n−1−i] を計算し、N で剰余を取る
    • sum_min = 2 × sum_min + A[i] を計算し、N で剰余を取る
  5. (sum_max − sum_min + N) mod N を返す

実装例

以下は Python による実装です。

N = 1000000007

def get_max_min_diff(A):
    n = len(A)
    A.sort()
    sum_min = 0
    sum_max = 0
    for i in range(n):
        sum_max = 2 * sum_max + A[n - 1 - i]
        sum_max %= N
        sum_min = 2 * sum_min + A[i]
        sum_min %= N
    return (sum_max - sum_min + N) % N

A = [1, 3, 4]
print(get_max_min_diff(A))

入力

[1, 3, 4]

出力

9

計算量について

この手法では、ソートに O(n log n)、集計ループに O(n) しかかかりません。全部分集合を列挙する O(2n) の方法と比べ、非常に高速に処理できるのが大きな利点です。

  1. Pythonで配列の合計を求める方法を徹底解説

    この記事では、Pythonを使って配列(リスト)の合計を求める方法について詳しく解説します。 問題文 問題: 配列が与えられたとき、その配列に含まれるすべての要素の合計を計算してください。 最も基本的なアプローチは、配列全体を走査し、各インデックスの要素を順番に加算していく方法です。ここでは、まず組み込み関数を活用したシンプルな実装例を見ていきましょう。 方法1:組み込み関数 sum() を使う Pythonには、イテラブルなオブジェクトの合計を一発で計算できる組み込み関数 sum() が用意されています。これを使えば、コードは非常に簡潔になります。 サンプルコード # 合計を求める関数 de

  2. Pythonで配列(リスト)の合計を求める方法をわかりやすく解説

    この記事では、配列(リスト)の合計値を求めるという問題に対して、Pythonでの解決策とアプローチをわかりやすく解説します。 問題の定義 配列が入力として与えられたとき、その配列に含まれるすべての要素の合計を計算することを目標とします。 例えば、[1, 2, 3, 4, 5] という配列が与えられた場合、出力は 15 になります。 アプローチ1:ループを使った素朴な方法(総当たり法) 最も基本的な方法は、リストを先頭から順に走査し、各要素を合計用の変数に加算していくやり方です。手順は以下の通りです。 合計を格納する変数を 0 で初期化します。 for ループでリストの各要素を取り出し、順番に