11

問題:

数値 n が与えられた場合、組み合わせの積の値でソートされたセット {1...n} から 2 つの組み合わせのリストを取得する効率的なアルゴリズムはありますか?

これは、特定の条件を満たす 2 つの * 桁の数値の最大の積を決定するために必要です。リストがソートされていない場合、最初に条件を満たすすべての組み合わせを決定し、それらを反復処理して最大の積を持つ組み合わせを見つける必要がありますが、これは非効率的です。

例として、n = 3 の場合、可能な組み合わせは次のとおりです。

Combination:      Product:
   3, 3              9
   3, 2              6
   3, 1              3
   2, 2              4
   2, 1              2
   1, 1              1

製品の値で降順に並べ替えると、次のようになります。

Combination:      Product:
   3, 3              9
   2, 3              6
   2, 2              4
   1, 3              3
   1, 2              2
   1, 1              1

余分な背景:

2 つの 3 桁の数の積である最大の回文数を見つけることに関する Project Euler の質問を解決しました。私のアプローチは、2 つの因数を使用して 999 (最大の 3 桁の数字) から下方に反復し、各組み合わせの積を見つけ、さらに数字が回文であるかどうかを確認することでした。

def maxpal():
    for i in reversed(range(100,1000)):

        # Since we only want unique combinations, we only
        # need to iterate up to i

        for j in reversed(range(100,i)):   
            if str(i*j) == str(i*j)[::-1]:
                yield i*j

print max(maxpal())

例の最初のリストは、このコードとまったく同じ順序で因子を反復することに注意してください。私の最初の仮定は、下向きに繰り返していたので、最初に見つけた回文が最大のものになるというものでした。デクリメントjされる前に 100 まで反復するため、これは明らかに当てはまりません。i

値が降順で生成されるように反復する方法を探しています。これにより、next(maxpal)1 回呼び出すだけで答えを取得できるため、はるかに効率的です。

編集:

Python 以外の言語での適切な回答を失格にしないために、私 (または他の誰か) が十分に理解できるように説明していただければ、どの言語でも問題ありません。

4

5 に答える 5

8

ヒープ/プライオリティ Q を使用できます。

(n,n) から始めて、ヒープに挿入します。比較機能 = 製品を比較します。

(x,y) を抽出するときはいつでも、必要に応じて (x-1,y) と (x,y-1) を挿入します (必要に応じてハッシュテーブルを維持して重複をチェックできます)。

上記を示す簡単な (そして見た目が悪い) コードを次に示します。これは遅延反復子であり、条件が満たされるとすぐに次の処理を実行して停止できることに注意してください。(注: larsman の提案 (以下のコメント) を使用すると改善されますが、考え方は似ています)

import heapq

def mult_comb(n):
    heap = []
    visited = {}
    visited[n*n] = True
    prod = n*n
    heapq.heappush(heap, (-prod, n, n))
    while prod > 1:
        (prod,x,y) = heapq.heappop(heap)
        yield -prod,x,y
        prod = -prod

        prod1 = (x-1)*y
        prod2 = x*(y-1)
        if not prod1 in visited:
            heapq.heappush(heap, (-prod1, x-1,y))
            visited[prod1] = True
        if not prod2 in visited:
            heapq.heappush(heap, (-prod2, x,y-1))
            visited[prod2] = True

def main():
    for tup in mult_comb(10):
        print tup

if __name__ == "__main__":
    main()
于 2013-03-21T01:12:19.107 に答える
3

質問のループスキーマは次のようになります

for i in reversed(range(100,1000)):
    for j in reversed(range(100,i)):   
        if str(i*j) is palindromic, yield i*j

要求された解決策は、そのループ テストと同じ数を降順に配信する方法を見つけることです。上記のコードは、404550 個の i,j ペアを生成します。これらのペアのうち 1231 はパリンドロームです。ペアのうち 2180 は、最終結果 906609 = 913*993 よりも大きいです。

これまでに提案された方法は、可能なペアのすべてまたは多くを生成する可能性があります。また、可能なペアの数しか生成しないものでも、必要以上に多くの数のペアをテストします。

対照的に、次のコードは 572 ペアのみをテストし、そのうち 3 つは回文です。これは主に 2 つの観察結果に依存します。1 つ目は、任意の 6 桁の回文は 11 の倍数です。これは、数字形式の任意の数abccbaが に等しいためa*100001 + b*10010 + c*1100です。100001、10010、1100 の 3 つすべてが 11 の倍数です。これまでのところ値 k があり、 i の特定の値をテストしているので、 anyまたは anyi≤jをテストする必要はありません。j < k/ij<i

def pal():
    nTop = 1000;    best, jin, jpal = 0, 0, 0
    # Test pairs (i, j) with i <= j
    for i in range(nTop, nTop//10-1, -1):
        jDel = 11 if i%11 else 1
        jHi = (nTop//jDel)*jDel
        jLo = max(i, best//i) - 1;
        for j in range(jHi, jLo, -jDel):
            jin += 1
            if str(i*j)==str(i*j)[::-1] :
                jpal += 1
                best = max(best, i*j)
    return (best, jin, jpal)

上記のコードでpal()は、タプル (906609, 572, 3) を返します。

于 2013-03-21T05:32:28.157 に答える
1

次のようにセットを生成できます。

>>> n=3
>>> s={(min(x,y),max(x,y)) for x in range(1,n+1) for y in range(1,n+1)}
>>> s
set([(1, 2), (1, 3), (3, 3), (2, 3), (2, 2), (1, 1)])

そして、次のように並べ替えます。

>>> sorted(s,key=lambda t: -t[0]*t[1])
[(3, 3), (2, 3), (2, 2), (1, 3), (1, 2), (1, 1)]

しかし、このようにする必要はまったくありません。ネストされた内包表記を使用するだけです:

>>> [(x,y) for x in range(3,0,-1) for y in range(3,x-1,-1)]
[(3, 3), (2, 3), (2, 2), (1, 3), (1, 2), (1, 1)]

これは、その特定の問題のワンライナーにつながります。

print max(x*y for x in range(1000,100,-1) for y in range(1000,x-1,-1) 
          if str(x*y)==str(x*y)[::-1])

提案した方法で本当にやりたい場合は、次を使用できますbisect

def PE4():
    import bisect

    def ispal(n):
        return str(n)==str(n)[::-1]

    r=[]
    for x in xrange(1000,100,-1):
        for y in xrange(1000,x-1,-1):
            if ispal(x*y): bisect.insort(r,(x*y,x,y))

    return r[-1]

bisect でサポートされている唯一の順序であるため、リストrは最終的に昇順になります。

次のものも使用できますheapq

def PE4_4():
    import heapq

    def ispal(n): return str(n)==str(n)[::-1]

    r=[]
    for x in xrange(100,1001):
        for y in xrange(x,1001):
            if ispal(x*y): heapq.heappush(r,(-x*y,x,y))     

    return (-r[0][0],r[0][1],r[0][2])   

これらの時間を計ると:

import timeit

def PE4_1():
    def ispal(n): return str(n)==str(n)[::-1]
    return max((x*y,x,y) for x in xrange(1000,99,-1) for y in xrange(1000,x-1,-1) if ispal(x*y))

def PE4_2():
    import bisect
    def ispal(n): return str(n)==str(n)[::-1]
    r=[]
    for x in xrange(1000,99,-1):
        for y in xrange(1000,x-1,-1):
            if ispal(x*y): bisect.insort(r,(x*y,x,y))

    return r[-1]

def PE4_3():
    import bisect
    def ispal(n): return str(n)==str(n)[::-1]
    r=[]
    for x in xrange(100,1001):
        for y in xrange(x,1001):
            if ispal(x*y): bisect.insort(r,(x*y,x,y))

    return r[-1]

def PE4_4():
    import heapq
    def ispal(n): return str(n)==str(n)[::-1]
    r=[]
    for x in xrange(100,1001):
        for y in xrange(x,1001):
            if ispal(x*y): heapq.heappush(r,(-x*y,x,y))     

    return (-r[0][0],r[0][1],r[0][2])         

n=25
for f in (PE4_1,PE4_2,PE4_3,PE4_4):
    fn=f.__name__
    print fn+':'
    print '\t',f()
    res=str(timeit.timeit('{}()'.format(fn),setup="from __main__ import {}".format(fn), number=n))
    print '\t'+res+' seconds\n'

それは印刷します:

PE4_1:
    (906609, 913, 993)
    10.9998581409 seconds

PE4_2:
    (906609, 913, 993)
    10.5356709957 seconds

PE4_3:
    (906609, 913, 993)
    10.9682159424 seconds

PE4_4:
    (906609, 913, 993)
    11.3141870499 seconds

メソッドがわずかに高速であることを示しbisect、その後にジェネレーターの最大値が続きます。heapq最も遅い方法です(わずかに)

長い答えですが、おそらく、必要なリストの順序を生成する最良の方法は、そのように並べ替えることです。


私は Knooth のソリューションの時間を測定しましたが、制約のある最初の数を見つけることは非常に優れています。

def PE4_6():
    def ispal(n): return str(n)==str(n)[::-1]
    def gen(n=1000):
        heap=[]
        visited=set([n*n])
        prod=n*n
        heapq.heappush(heap,(-prod,n,n))
        while abs(prod)>1:
            (prod,x,y)=heapq.heappop(heap)
            yield -prod,x,y
            p1,p2=(x-1)*y, x*(y-1)
            if p1 not in visited:
                heapq.heappush(heap, (-p1, x-1,y))
                visited.add(p1)
            if p2 not in visited:
                heapq.heappush(heap, (-p2, x,y-1))
                visited.add(p2)

    it=iter(gen())
    t=next(it)
    while not ispal(t[0]):
        t=next(it)

    return t   

ただし、リスト全体を見つけるのは遅くなります。

于 2013-03-21T01:35:07.390 に答える
0

数値 n が与えられた場合、組み合わせの積の値でソートされたセット {1...n} から 2 つの組み合わせのリストを取得する効率的なアルゴリズムはありますか?

あなたが何を求めているのかよくわかりませんが、これはPythonでコーディングする簡単な方法です:

n = SOME_INTEGER
from itertools import combinations
sorted(combinations(set(xrange(1,n+1)),2),key=lambda x: x[0]*x[1])

または、最大の製品が最初に:

sorted(combinations(set(xrange(1,n+1)),2),key=lambda x: x[0]*x[1],reverse=True)
于 2013-03-21T00:58:18.933 に答える
0

b > c の場合、(a, b) は常に (a, c) の前に来ることがわかります。したがって、各クラス [(a, b), (a, b-1), (a, b-2), ...] の代表者を 1 人だけ保持し、これらの中から選択することができます。ヒープを使用します。この実装には、O(n^2*log(n)) 時間と O(n) スペースが必要です。

import heapq

def combinations_prod_desc(n):
    h = [(-i*i, i, i) for i in xrange(1, n+1)]
    h.reverse()

    while len(h) > 0:
        u = h[0]
        yield u
        b = u[2]
        if b <= 1:
            heapq.heappop(h)
            continue
        a = u[1]
        b -= 1
        heapq.heappushpop(h, (-a*b, a, b))
    return

Python 2.6 以降、heapq モジュールにはマージ アルゴリズムが組み込まれています。それを利用して、同じアルゴリズムの 1 行の実装を取得できます。

def combinations_prod_desc_compact(n):
    return heapq.merge(*[(lambda a : ((-a*b, a, b) for b in xrange(a, 0, -1)))(a) for a in xrange(1, n+1)])

上記の次の単純なバージョンは、Python 内包表記のセマンティクスがおかしいため、機能しません。誰かが Python の言語仕様を調べることに興味を持っている場合は、次のコードが「あるべき」ように見えても、必要な結果が得られない正確な理由を調べてみると興味深いでしょう。

def combinations_prod_desc_nonworking(n):
    return heapq.merge(*[((-a*b, a, b) for b in xrange(a, 0, -1)) for a in xrange(1, n+1)])
于 2013-03-21T01:37:38.737 に答える