Pythonで指示確率変数から関数Fの期待値を計算するプログラムの実装方法
問題の概要
2つの整数 k と n が与えられているとします。1 から n までの自然数 {1, 2, ..., n} のランダムな順列 p1, p2, ..., pn を考え、次の値 F を計算します。
F = (X2 + ... + Xn−1)k
ここで Xi は指示確率変数(インジケーター確率変数)であり、以下のいずれかの条件が成り立つときに 1、それ以外のときに 0 を取ります。
- pi−1 < pi > pi+1(局所的な極大となる場合)
- pi−1 > pi < pi+1(局所的な極小となる場合)
このとき、F の期待値 E[F] を求めることが目標です。
たとえば、入力が k = 1、n = 1000 の場合、出力は 1996/3 になります。
解法のアプローチ
この問題は、k の値ごとに事前に導出された多項式(閉形式)を利用することで、効率的に答えを求められます。全体の流れは次の通りです。
- 関数
exp_factor()を定義します。引数として n と k を受け取ります。 - k の値に応じて、対応する多項式の「分子」と「分母」のペアを返します。
- メイン処理では、分子と分母の最大公約数(GCD)で約分し、既約分数の形で結果を返します。
exp_factor() の戻り値一覧
- k = 1 の場合:(2×(n−2), 3) を返す
- k = 2 の場合:(40n² − 144n + 131, 90) を返す
- k = 3 の場合:(280n³ − 1344n² + 2063n − 1038, 945) を返す
- k = 4 の場合:(2800n⁴ − 15680n³ + 28844n² − 19288n + 4263, 14175) を返す
- k = 5 の場合:(12320n⁵ − 73920n⁴ + 130328n³ − 29568n² − 64150n − 5124, 93555) を返す
- 上記以外の場合:1.0 を返す
メイン処理の手順
- M := n − 2
- p := 2.0 / 3
- q := 1 − p
- (num, den) := exp_factor(n, k)
- g := gcd(num, den)
- 分数 (num/g) / (den/g) を文字列として返す
なぜ p = 2/3 なのか?
連続する 3 つの要素 pi−1, pi, pi+1 は互いに異なる値を持つため、その大小関係の並び方は 3! = 6 通りあり、すべて同じ確率で現れます。このうち、中央の要素が極大または極小になる(山または谷の形になる)のは 6 通りのうち 4 通りです。したがって、P(Xi = 1) = 4/6 = 2/3 となります。これが p = 2/3 という設定の由来です。
実装例(Python)
以下のコードで実際の動作を確認できます。
from math import gcd
def exp_factor(n, k):
if k == 1:
return (2*(n-2), 3)
elif k == 2:
return (40*n**2 - 144*n + 131, 90)
elif k == 3:
return (280*n**3 - 1344*n**2 + 2063*n - 1038, 945)
elif k == 4:
return (2800*n**4 - 15680*n**3 + 28844*n**2 - 19288*n + 4263, 14175)
elif k == 5:
return (12320*n**5 - 73920*n**4 + 130328*n**3 - 29568*n**2 - 64150*n - 5124, 93555)
return 1.0
def solve(k, n):
M = n - 2
p = 2.0 / 3
q = 1 - p
num, den = exp_factor(n, k)
g = gcd(num, den)
return str(int(num/g)) + '/' + str(int(den/g))
k = 1
n = 1000
print(solve(k, n))入力
1, 1000
出力
1996/3
まとめ
このプログラムでは、順列の中で各位置が局所的な極大・極小になる事象を指示確率変数で表し、その k 乗和の期待値を k ごとの閉形式多項式で計算しています。GCD による約分を行うことで、結果を常に既約分数として出力できる点がポイントです。
-
Pythonで数列「n + nn + nnn + …」をm回計算するプログラムの作成方法
この記事では、正の整数 n と繰り返し回数 m が与えられたとき、n + nn + nnn + … のように同じ数字を連結した項を m 個足し合わせた数列の合計を求める Python プログラムを解説します。例えば、n = 3、m = 5 の場合、計算対象となる数列は以下の通りです。3 + 33 + 333 + 3333 + 33333 = 37035アルゴリズム処理の手順は以下のようになります。n と m を入力として受け取る。数値 n を文字列に変換する。合計値と文字列を初期化する。残りの項を順番に足し合わせていく。文字列を連結して n、nn、nnn… を生成する。加算する前に、文字列を整
-
Pythonで数値の特定の位置から「k」ビットを抽出する方法
この関数は、指定した位置(pos)から「k」個のビットを抽出し、その抽出結果を返すために使用します。ここでは、Pythonのスライス(slicing)という手法を活用して実装していきます。 実行例 入力:number = 170 K = 5 Pos = 2 出力:21 上記の例では、10進数の「170」を2進数に変換すると「10101010」となります。右端から2番目の位置を起点として、そこから左方向に5ビットを抽出すると「10101」になり、これを10進数に戻すと「21」になります。 アルゴリズム Extractionbit(no, k, pos) /* ユーザ