2012-06-12 19 views
7

Bugüne kadar bu var, aralıkları listesinden itertools içeren bir liste oluşturma: ŞimdiPython itertools.product ile bir liste oluşturuyor?

start_list = [xrange(0,201,1),xrange(0,201,2),xrange(0,201,5),xrange(0,201,10),xrange(0,201,20),xrange(0,201,50),xrange(0,201,100),xrange(0,201,200)] 

, ben bu sonraki satırı çalıştırın çalışabilir olsaydı benim piton tercüman öldürecek biliyorum :

next_list = list(itertools.product(*start_list)) 

ne onun öğelerin toplamı için, her başlığın denetleyen bir argüman koymak mümkün olacağını edilir merak ediyorum ve eşit eğer sadece belirli bir miktara next_list koyar?

Belki böyle bir şey:

next_list = list(itertools.product(*start_list,sum(tuples)=200)) 

Bunun doğru olmadığını biliyorum ve ben bu konuda gidiyorum şekilde yeniden düşünmeye başlamak gerekebilir. Start_list'in jeneratördeki aralıkları başka bir liste oluşturmak için geçecek kadar çok mu olacak?

+4

Tamsayı 200'ün farklı kümelerden elde edilen 8 terime nasıl bölüneceğini anlamaya çalışıyorsanız, sonraki_listeyi hesaplamanın daha kolay yolları vardır. Doğru sayıyorsam Kartezyen ürününüzün üzerinde yinelenecek olan 5768123130 farklı öğeleri vardır, bu da oldukça zaman alacaktır. – DSM

+0

Merhaba DSM, yanıt verdiğiniz için teşekkürler. Daha verimli bir yöntem oluşturmaya çalışacağım. – tijko

+0

ile ilgili: http://stackoverflow.com/questions/1106929/find-all-combinations-of-coins-when-given-some-dollar-value – jfs

cevap

12

Daha iyi sadece bir listesini kullanmak için anlama

new_list = [item for item in itertools.product(*start_list) if sum(item) == 200] 
+0

Çözümünüz niyetinizi daha net hale getiriyor. –

+0

Hey gnibbler, yanıt verdiğin için teşekkürler. Ben bunu i (itertools.product (* start_list)) listesinden denedim: eğer toplam (i) == 200: new_list.append (i) 'Tercüman da çöktü. Şimdi bu sorunu çözmek için farklı bir yol bulmam gerekiyor olsa bile, yorumunuzdan öğrendim! – tijko

1

bunu kullanın:

next_list = listesi (itertools.product (öğenin öğe * START_LIST) toplamı (madde varsa) == 200)

+0

@gnibbler: Sanırım değişken ismini yanlış yazıyorsunuz;) –

+0

Oh Tabii, _I'm_ yeterince kahve içmemiş olan kişi –

+0

@gnibbler: Benim 12. kupamdayım. İstiridye ve şarap için kapalı :) –

2
Solution  Runtime   Fn calls   Lines of Code 
-------- ---------- ------------------------ ------------- 
gnibbler 2942.627 s 1473155845 (1.5 billion)   1 
me_A   16.639 s  28770812 (29 million)   5 
me_B   0.452 s  774005 (.8 million)   43 

Çözüm me_A:

import itertools 

def good_combos(basis, addto): 
    good_sums = set(addto-b for b in basis[0]) 
    return ([addto-sum(items)]+list(items) for items in itertools.product(*basis[1:]) if sum(items) in good_sums) 

next_list = list(good_combos(start_list, 200)) 

bunu ilk en uzun listesi iletirseniz buçok daha hızlı olabilir unutmayın etmeyin.

Bu çözüm, bir düzey yinelemeyi ayarlanmış bir arama ile değiştirir; 200 öğeyi içeren en uzun listenizle, bunun neredeyse 200 kat daha hızlı olması şaşırtıcı olmamalıdır.


Çözüm me_B: Ancak daha genel olarak - - Her liste herhangi kaldıraç uğratıldığında hem 0 hem de addto içeren

import itertools 
from bisect import bisect_left, bisect_right 

def good_combos(addto=0, *args): 
    """ 
    Generate all combinations of items from a list of lists, 
    taking one item from each list, such that sum(items) == addto. 

    Items will only be used if they are in 0..addto 

    For speed, try to arrange the lists in ascending order by length. 
    """ 
    if len(args) == 0:       # no lists passed? 
     return [] 

    args = [sorted(set(arg)) for arg in args] # remove duplicate items and sort lists in ascending order 
    args = do_min_max(args, addto)    # use minmax checking to further cull lists 

    if any(len(arg)==0 for arg in args):  # at least one list no longer has any valid items? 
     return [] 

    lastarg = set(args[-1]) 
    return gen_good_combos(args, lastarg, addto) 

def do_min_max(args, addto, no_negatives=True): 
    """ 
    Given 
     args   a list of sorted lists of integers 
     addto   target value to be found as the sum of one item from each list 
     no_negatives if True, restrict values to 0..addto 

    Successively apply min/max analysis to prune the possible values in each list 

    Returns the reduced lists 
    """ 
    minsum = sum(arg[0] for arg in args) 
    maxsum = sum(arg[-1] for arg in args) 

    dirty = True 
    while dirty: 
     dirty = False 
     for i,arg in enumerate(args): 
      # find lowest allowable value for this arg 
      minval = addto - maxsum + arg[-1] 
      if no_negatives and minval < 0: minval = 0 
      oldmin = arg[0] 
      # find highest allowable value for this arg 
      maxval = addto - minsum + arg[0] 
      if no_negatives and maxval > addto: maxval = addto 
      oldmax = arg[-1] 

      if minval > oldmin or maxval < oldmax: 
       # prune the arg 
       args[i] = arg = arg[bisect_left(arg,minval):bisect_right(arg,maxval)] 
       minsum += arg[0] - oldmin 
       maxsum += arg[-1] - oldmax 
       dirty = True 
    return args 

def gen_good_combos(args, lastarg, addto): 
    if len(args) > 1: 
     vals,args = args[0],args[1:] 
     minval = addto - sum(arg[-1] for arg in args) 
     maxval = addto - sum(arg[0] for arg in args) 
     for v in vals[bisect_left(vals,minval):bisect_right(vals,maxval)]: 
      for post in gen_good_combos(args, lastarg, addto-v): 
       yield [v]+post 
    else: 
     if addto in lastarg: 
      yield [addto] 

basis = reversed(start_list) # more efficient to put longer params at end 
new_list = list(good_combos(200, *basis)) 

do_min_max() gerçekten verilerinizin set şey başaramayız veri problem boyutunu büyük ölçüde azaltabilir. Buradaki tasarruf, yinelemenin her düzeyinde (ağaç budaması) göz önüne alınan maddelerin sayısını art arda azaltarak bulunur.

+0

Kodunuzun yazılması 50 dakika sürdüyse, yine de kazanırdım :). Cidden cevabım sadece orijinal problemi ele alıyordu, 1 dakika kuralı değil –

+1

@gnibbler: Kısa versiyondan önce 3 saat daha uzun süredir oynamaya başladım. –

+0

Her ikisi de çok yardımcı olacak, ikisinden de öğreneceğim: D – tijko