2012-06-12 10 views
7

Sto creando una lista con itertools da un elenco di intervalli, fino ad ora ho questo:Python crea una lista con itertools.product?

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)] 

Ora, so che se dovessi provare ad eseguire questo riga successiva si ucciderà la mia interprete Python :

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

quello che mi chiedo è: sarebbe il possibile mettere in un argomento che controlla ogni tupla, per una somma delle sue voci e li mette solo in next_list se uguale a una certa quantità?

Forse qualcosa di simile:

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

So che questo non è giusto e potrei aver bisogno di iniziare a ripensare il modo in cui sto andando su questo. Le gamme di start_list nel generatore saranno troppe per passare alla creazione di un altro elenco?

+4

Se si sta tentando di capire come partizionare il numero intero da 200 a 8 termini tratti da diversi set, ci sono modi più semplici per calcolare next_list. Se conto correttamente che il tuo prodotto cartesiano ha 5768123130 elementi distinti da iterare, ci vorrà un po 'di tempo. – DSM

+0

Ciao DSM, grazie per la risposta. Cercherò di creare un metodo più efficiente. – tijko

+0

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

risposta

12

Meglio utilizzare solo un elenco comprensione

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

La tua soluzione rende l'intento molto più chiaro. –

+0

Hey gnibbler, grazie per aver risposto. Ho provato questo 'per i in elenco (itertools.product (* start_list)): if sum (i) == 200: new_list.append (i)' Che ha anche causato il crash dell'interprete. Ora, anche se ho bisogno di trovare un modo diverso per risolvere questo problema, ho imparato dal tuo commento, grazie! – tijko

1

Utilizzare questa:

next_list = lista (elemento per elemento in itertools.product (* START_LIST) se sum (voce) == 200)

+0

@gnibbler: Penso che tu abbia interpretato erroneamente il nome della variabile;) –

+0

Oh certo, _I'm_ colui che non ha bevuto abbastanza caffè –

+0

@gnibbler: Sono sulla mia 12a tazza me stesso. Off per ostriche e vino :) –

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 

Soluzione 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)) 

Do atto che questo può essere molto più veloce se si passa la lista più lunga prima.

Questa soluzione sostituisce un livello di iterazione con una ricerca set; con la tua lista più lunga contenente 200 articoli, non dovrebbe sorprendere che questo è quasi 200 volte più veloce.


Soluzione me_B:

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() davvero non si può realizzare nulla sul set di dati - ogni lista contiene sia addto 0 e, privandolo di qualsiasi leva - tuttavia su base più generale i dati possono ridurre notevolmente la dimensione del problema.

I risparmi qui si trovano nel ridurre progressivamente il numero di articoli considerati a ogni livello di iterazione (potatura di alberi).

+0

Se il tuo codice impiegava 50 minuti per scrivere, avrei comunque vinto :). Seriamente la mia risposta è stata solo quella di risolvere il problema originale, non la regola di 1 minuto –

+1

@gnibbler: più come 3 ore di gioco con la versione lunga prima che mi venisse in mente la versione breve. –

+0

Entrambi molto utili, imparerò da entrambi: D – tijko

Problemi correlati