2

セット内の整数の間に発生する可能性のある正の整数の数を減らすために、厳密に増加する (または厳密に減少する) 正の整数 P のセットをどのように再コーディングしますか?

なぜこれを行う必要があるのでしょうか: P をランダムにサンプリングしたいとしますが、1.) P は大きすぎて列挙できません。2.) P のメンバーは非ランダムな方法で関連付けられていますが、サンプリングするには複雑すぎます。に。ただし、P のメンバーは、見ればわかります。P[0] と P[n] はわかっているが、P のすべてを列挙したり、P のメンバーがどのように関連しているかを正確に理解したりするという考えを受け入れることができないとします。同様に、P[0] と P[n] の間で発生するすべての可能な整数の数は、P のサイズの何倍も大きく、P のメンバーをランダムに描画する可能性は非常に小さくなります。

例: P[0] = 2101010101 & P[n] = 505050505 とします。おそらく、特定の品質を持つ P[0] と P[n] の間の整数 (たとえば、P[x 内のすべての整数) ] の合計が Q 以下になる場合、P の各メンバーは最大の整数として 7 以下になります)。したがって、すべての正の整数 P[n] <= X <= P[0] が P に属するわけではありません。私が興味を持っている P については、以下のコメントで説明します。

私が試したこと: P が厳密に減少するセットであり、P[0] と P[n] がわかっている場合、各メンバーを P[0] から減算されたかのように扱うことができます。そうすることで、各数値がおそらく大幅に減少し、各メンバーが一意の整数として維持されます。私が興味を持っている P (以下) については、P の各減少値を共通の分母 (9,11,99) で割ったものとして扱うことができます。これにより、P のメンバー間で可能な整数の数が減少します。これらのアプローチを組み合わせて使用​​すると、すべての P[0] <= X <= P[n] のセットが数桁減少し、すべての正の整数 P[n ] <= X <= P[0] まだ非常に小さいです。

: 明らかなように、P について何かを知っている必要があります。そうでない場合、それは基本的に、何を探しているかの手がかりがないことを意味します。P[0] と P[n] (記録されているかどうかに関係なく) の間の整数をランダムにサンプリングするとき、実際にそうである場合、「うん、それは P に属している」と言うことができる必要があります。

良い答えは、私が開発したコンピューティング アルゴリズムの実用的なアプリケーションを大幅に向上させる可能性があります。私が興味を持っている種類の P の例は、コメント 2 に示されています。

4

2 に答える 2

1

元の質問は整数エンコーディングに関する非常に一般的なシナリオについて尋ねていますが、完全に一般的に機能するアプローチが存在する可能性は低いと思います。たとえば、P[i] が多かれ少なかれランダムである場合 (情報理論の観点から)、何かがうまくいくとは驚きです。

代わりに、正確に K 個の部分を含む整数 N のパーティションを生成するという OP の実際の問題に注意を向けましょう。組み合わせオブジェクトを整数としてエンコードする場合、可能な限り多くの組み合わせ構造を保持する必要があります。このために、Nijenhuis と Wilf による古典的なテキストCombinatorial Algorithms、具体的には第 13 章に目を向けます。実際、この章では、彼らは多数の組み合わせファミリから列挙およびサンプリングするためのフレームワークを示しています。一部は K に等しいです。K 個の部分を持つパーティションと最大部分が K であるパー​​ティション (フェラーズ図の転置を取る) の間のよく知られた双対性を使用すると、復号化プロセスに変更を加えるだけでよいことがわかります。

とにかく、ここにいくつかのソースコードがあります:

import sys
import random
import time

if len(sys.argv) < 4 :
    sys.stderr.write("Usage: {0} N K iter\n".format(sys.argv[0]))
    sys.stderr.write("\tN = number to be partitioned\n")
    sys.stderr.write("\tK = number of parts\n")
    sys.stderr.write("\titer = number of iterations (if iter=0, enumerate all partitions)\n")
    quit()

N = int(sys.argv[1])
K = int(sys.argv[2])
iters = int(sys.argv[3])

if (N < K) :
    sys.stderr.write("Error: N<K ({0}<{1})\n".format(N,K))
    quit()

# B[n][k] = number of partitions of n with largest part equal to k
B = [[0 for j in range(K+1)] for i in range(N+1)] 

def calc_B(n,k) :
    for j in xrange(1,k+1) :
        for m in xrange(j, n+1) :
            if j == 1 :
                B[m][j] = 1
            elif m - j > 0 :
                B[m][j] = B[m-1][j-1] + B[m-j][j]
            else :
                B[m][j] = B[m-1][j-1]

def generate(n,k,r=None) :
    path = []
    append = path.append

    # Invalid input
    if n < k or n == 0 or k == 0: 
        return []

    # Pick random number between 1 and B[n][k] if r is not specified
    if r == None :
        r = random.randrange(1,B[n][k]+1)

    # Construct path from r    
    while r > 0 :
        if n==1 and k== 1:
            append('N')
            r = 0   ### Finish loop
        elif r <= B[n-k][k] and B[n-k][k] > 0  : # East/West Move
            append('E')
            n = n-k
        else : #  Northeast/Southwest move
            append('N')
            r -= B[n-k][k]
            n = n-1
            k = k-1

    # Decode path into partition    
    partition = []
    l = 0
    d = 0    
    append = partition.append    
    for i in reversed(path) :
        if i == 'N' :
            if d > 0 : # apply East moves all at once
                for j in xrange(l) :
                    partition[j] += d
            d = 0  # reset East moves
            append(1) # apply North move
            l += 1            
        else :
            d += 1 # accumulate East moves    
    if d > 0 : # apply any remaining East moves
        for j in xrange(l) :
            partition[j] += d

    return partition


t = time.clock()
sys.stderr.write("Generating B table... ")    
calc_B(N, K)
sys.stderr.write("Done ({0} seconds)\n".format(time.clock()-t))

bmax = B[N][K]
Bits = 0
sys.stderr.write("B[{0}][{1}]: {2}\t".format(N,K,bmax))
while bmax > 1 :
    bmax //= 2
    Bits += 1
sys.stderr.write("Bits: {0}\n".format(Bits))

if iters == 0 : # enumerate all partitions
    for i in xrange(1,B[N][K]+1) :
        print i,"\t",generate(N,K,i)

else : # generate random partitions
    t=time.clock()
    for i in xrange(1,iters+1) :
        Q = generate(N,K)
        print Q
        if i%1000==0 :
            sys.stderr.write("{0} written ({1:.3f} seconds)\r".format(i,time.clock()-t))

    sys.stderr.write("{0} written ({1:.3f} seconds total) ({2:.3f} iterations per second)\n".format(i, time.clock()-t, float(i)/(time.clock()-t) if time.clock()-t else 0))

パフォーマンスの例を次に示します (MacBook Pro 8.3、2GHz i7、4 GB、Mac OSX 10.6.3、Python 2.6.1):

mhum$ python part.py 20 5 10
Generating B table... Done (6.7e-05 seconds)
B[20][5]: 84    Bits: 6
[7, 6, 5, 1, 1]
[6, 6, 5, 2, 1]
[5, 5, 4, 3, 3]
[7, 4, 3, 3, 3]
[7, 5, 5, 2, 1]
[8, 6, 4, 1, 1]
[5, 4, 4, 4, 3]
[6, 5, 4, 3, 2]
[8, 6, 4, 1, 1]
[10, 4, 2, 2, 2]
10 written (0.000 seconds total) (37174.721 iterations per second)

mhum$ python part.py 20 5 1000000 > /dev/null
Generating B table... Done (5.9e-05 seconds)
B[20][5]: 84    Bits: 6
100000 written (2.013 seconds total) (49665.478 iterations per second)

mhum$ python part.py 200 25 100000 > /dev/null
Generating B table... Done (0.002296 seconds)
B[200][25]: 147151784574    Bits: 37
100000 written (8.342 seconds total) (11987.843 iterations per second)

mhum$ python part.py 3000 200 100000 > /dev/null
Generating B table... Done (0.313318 seconds)
B[3000][200]: 3297770929953648704695235165404132029244952980206369173   Bits: 181
100000 written (59.448 seconds total) (1682.135 iterations per second)

mhum$ python part.py 5000 2000 100000 > /dev/null
Generating B table... Done (4.829086 seconds)
B[5000][2000]: 496025142797537184410324290349759736884515893324969819660    Bits: 188
100000 written (255.328 seconds total) (391.653 iterations per second)

mhum$ python part-final2.py 20 3 0
Generating B table... Done (0.0 seconds)
B[20][3]: 33    Bits: 5
1   [7, 7, 6]
2   [8, 6, 6]
3   [8, 7, 5]
4   [9, 6, 5]
5   [10, 5, 5]
6   [8, 8, 4]
7   [9, 7, 4]
8   [10, 6, 4]
9   [11, 5, 4]
10  [12, 4, 4]
11  [9, 8, 3]
12  [10, 7, 3]
13  [11, 6, 3]
14  [12, 5, 3]
15  [13, 4, 3]
16  [14, 3, 3]
17  [9, 9, 2]
18  [10, 8, 2]
19  [11, 7, 2]
20  [12, 6, 2]
21  [13, 5, 2]
22  [14, 4, 2]
23  [15, 3, 2]
24  [16, 2, 2]
25  [10, 9, 1]
26  [11, 8, 1]
27  [12, 7, 1]
28  [13, 6, 1]
29  [14, 5, 1]
30  [15, 4, 1]
31  [16, 3, 1]
32  [17, 2, 1]
33  [18, 1, 1]

OPに任せて、このコードが実際に目的の(均一な)分布に従ってパーティションを生成することを確認します。

編集:列挙機能の例を追加しました。

于 2013-02-17T22:13:48.247 に答える
0

以下は、K 部分で N の整数パーティションを表す整数を再コーディングする限り、私が求めたことを達成するスクリプトです。このアプローチが K > 4 の場合に実用的であるためには、より優れた再コーディング方法が必要です。しかし、それは概念的に単純であり、根本的に公平であると簡単に主張できます。小さな K に対しても非常に高速です。スクリプトは Sage ノートブックで問題なく実行され、Sage 関数は呼び出されません。これはランダム サンプリング用のスクリプトではありません。ランダム サンプリング自体は問題ではありません。

メソッド:

1.) 整数パーティションを、それらの被加数が連結され、最初のレキシカル パーティション内の最大の被加数のサイズに応じてゼロが埋め込まれているかのように扱います。たとえば、[17,1,1,1] -> 17010101 & [5,5,5,5 ] -> 05050505

2.) 結果の整数を、最大の整数 (つまり、最初の字句区分を表す int) から減算したかのように扱います。例: 17010101 - 5050505 = 11959596

3.) 減少した整数を共通の分母で割ったものとして扱います。たとえば、11959596/99 = 120804 です。

したがって、ランダムなパーティションを選択したい場合は、次のようにします。

1.) 0 から 120,804 の間の数値を選択します (5,050,505 から 17,010,101 の間の数値ではなく)

2.) 数値に 99 を掛けて、17010101 から引きます。

3.) 各整数を 0 で埋めたものとして処理した方法に従って、結果の整数を分割します。

長所と短所:質問の本文で述べたように、この特定の再コーディング方法では、P のメンバーを表す整数をランダムに選択する可能性を大幅に改善するのに十分ではありません。より大きな合計、たとえば N > 100 の場合、この概念を実装する関数は非常に高速になる可能性があります。これは、他のランダム分割関数を遅くしたり、大きな N を処理するために他の関数を非実用的にしたりするタイムリーな再帰 (ヘビが尻尾を食べる) を回避するためです。

K が小さい場合、P のメンバーを描画する確率は、残りのプロセスの速度を考慮すると妥当です。迅速なランダム描画、デコード、および評価と組み合わせることで、この関数は、他のアルゴリズムでは不可能な N&K (たとえば、N = 20000、K = 4) の組み合わせに対して均一なランダム パーティションを見つけることができます。これを一般的に強力なアプローチにするためには、整数を再コード化するためのより良い方法が大いに必要です。

import random
import sys

まず、いくつかの一般的に便利で簡単な関数

def first_partition(N,K):
    part = [N-K+1]
    ones = [1]*(K-1)
    part.extend(ones)
return part

def last_partition(N,K):
    most_even = [int(floor(float(N)/float(K)))]*K
    _remainder = int(N%K)
    j = 0

    while _remainder > 0:
        most_even[j] += 1
        _remainder -= 1
        j += 1
    return most_even

def first_part_nmax(N,K,Nmax):
    part = [Nmax]
    N -= Nmax
    K -= 1
    while N > 0:
        Nmax = min(Nmax,N-K+1)
        part.append(Nmax)
        N -= Nmax
        K -= 1

    return part


#print first_partition(20,4)
#print last_partition(20,4)
#print first_part_nmax(20,4,12)
#sys.exit()

def portion(alist, indices): 
    return [alist[i:j] for i, j in zip([0]+indices, indices+[None])]

def next_restricted_part(part,N,K): # *find next partition matching N&K w/out recursion

    if part == last_partition(N,K):return first_partition(N,K)
    for i in enumerate(reversed(part)):
        if i[1] - part[-1] > 1:
            if i[0] == (K-1):
                return first_part_nmax(N,K,(i[1]-1))

            else:
                parts = portion(part,[K-i[0]-1]) # split p
                h1 = parts[0]
                h2 = parts[1]
                next = first_part_nmax(sum(h2),len(h2),(h2[0]-1))
                return h1+next

""" *I don't know a math software that has this function and Nijenhuis and Wilf (1978) 
 don't give it (i.e. NEXPAR is not restricted by K). Apparently, folks often get the
 next restricted part using recursion, which is unnecessary """

def int_to_list(i): # convert an int to a list w/out padding with 0'
    return [int(x) for x in str(i)]

def int_to_list_fill(i,fill):# convert an int to a list and pad with 0's
    return [x for x in str(i).zfill(fill)]

def list_to_int(l):# convert a list to an integer
    return "".join(str(x) for x in l)

def part_to_int(part,fill):# convert an int to a partition of K parts
# and pad with the respective number of 0's

    p_list = []
    for p in part:
        if len(int_to_list(p)) != fill:
            l = int_to_list_fill(p,fill)
            p = list_to_int(l)
        p_list.append(p)
    _int = list_to_int(p_list)
    return _int       

def int_to_part(num,fill,K): # convert an int to a partition of K parts
    # and pad with the respective number of 0's
    # This function isn't called by the script, but I thought I'd include
    # it anyway because it would be used to recover the respective partition

    _list = int_to_list(num)
    if len(_list) != fill*K:
        ct = fill*K - len(_list) 
        while ct > 0:    
            _list.insert(0,0)
            ct -= 1    
    new_list1 = []
    new_list2 = []

    for i in _list:
        new_list1.append(i)
        if len(new_list1) == fill:
            new_list2.append(new_list1)
            new_list1 = []

    part = []
    for i in new_list2:
        j = int(list_to_int(i))
        part.append(j)

    return part

最後に、合計 N とパーツ数 K を取得します。以下は、関連する再コード化された整数とともに、N&K を満たすパーティションを字句順に出力します。

N = 20
K = 4

print '#,  partition,  coded,    _diff,    smaller_diff'

first_part = first_partition(N,K) # first lexical partition for N&K
fill = len(int_to_list(max(first_part)))
# pad with zeros to 1.) ensure a strictly decreasing relationship w/in P,
# 2.) keep track of (encode/decode) partition summand values

first_num = part_to_int(first_part,fill)
last_part = last_partition(N,K)
last_num = part_to_int(last_part,fill)

print '1',first_part,first_num,'',0,'      ',0

part = list(first_part)
ct = 1
while ct < 10:
    part = next_restricted_part(part,N,K)
    _num = part_to_int(part,fill)

    _diff = int(first_num) - int(_num)
    smaller_diff = (_diff/99)
    ct+=1

    print ct, part, _num,'',_diff,' ',smaller_diff

出力:

ct、パーティション、コード化、_diff、smaller_diff

1 [17, 1, 1, 1] 17010101 0 0

2 [16, 2, 1, 1] 16020101 990000 10000

3 [15、3、1、1] 15030101 1980000 20000

4 [15, 2, 2, 1] 15020201 1989900 20100

5 [14、4、1、1] 14040101 2970000 30000

6 [14、3、2、1] 14030201 2979900 30100

7 [14、2、2、2] 14020202 2989899 30201

8 [13、5、1、1] 13050101 3960000 40000

9 [13、4、2、1] 13040201 3969900 40100

10 [13、3、3、1] 13030301 3979800 40200

つまり、最後の列の整数は、はるかに小さい可能性があります。

この考えに基づくランダム サンプリング戦略が基本的に公平である理由:

K 個の部分を持つ N の各整数パーティションは、1 つの再コード化された整数にのみ対応します。つまり、数字を無作為に選んでデコードし、要素を再配置して N&K の適切な分割を形成しようとすることはありません。したがって、各整数 (N&K のパーティションに対応するかどうかに関係なく) が描画される可能性は同じです。目標は、N と K の部分に対応しない整数の数を本質的に減らすことであり、ランダム サンプリングのプロセスを高速化することです。

于 2013-02-18T20:52:03.813 に答える