Próbuję utworzyć parę funkcji, które, biorąc pod uwagę listę numerów "Uruchamiania", rekurencyjnie dodać do każdej pozycji indeksu do określonej wartości maksymalnej (większość w taki sam sposób, aby kilometr działa w samochodzie - każdy Koło przeciw liczniku zwiększa się do 9 przed zresetowaniem do 1 i noszenia na następne koło).

Kod wygląda następująco:

number_list = []

def counter(start, i, max_count):
    if start[len(start)-1-i] < max_count:
        start[len(start)-1-i] += 1
        return(start, i, max_count)
    else:
        for j in range (len(start)):
            if start[len(start)-1-i-j] == max_count:
                start[len(start)-1-i-j] = 1
            else:
                start[len(start)-1-i-j] += 1
                return(start, i, max_count)

def all_values(fresh_start, i, max_count):
    number_list.append(fresh_start)
    new_values = counter(fresh_start,i,max_count)
    if new_values != None:
        all_values(*new_values) 

Po uruchomieniu all_values ([1,1,1], 0,3) i wydrukuj Numer_list , dostaję:

[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],    
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], 
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], 
[1, 1, 1], [1, 1, 1], [1, 1, 1]]

Który jest niefortunny. Podwójnie tak, że wiedząc, że jeśli wymieniam pierwszą linię all_values z

print(fresh_start)

Dostaję dokładnie, co jestem po:

[1, 1, 1]
[1, 1, 2]
[1, 1, 3]
[1, 2, 1]
[1, 2, 2]
[1, 2, 3]
[1, 3, 1]
[1, 3, 2]
[1, 3, 3]
[2, 1, 1]
[2, 1, 2]
[2, 1, 3]
[2, 2, 1]
[2, 2, 2]
[2, 2, 3]
[2, 3, 1]
[2, 3, 2]
[2, 3, 3]
[3, 1, 1]
[3, 1, 2]
[3, 1, 3]
[3, 2, 1]
[3, 2, 2]
[3, 2, 3]
[3, 3, 1]
[3, 3, 2]
[3, 3, 3]

Próbowałem już wykonać kopię Fresh_start (za pomocą TEMP = Fresh_start ) i dołączając, że zamiast tego, ale bez zmian w wyjściu.

Czy ktoś może zaoferować wgląd, co mogę zrobić, aby naprawić mój kod? Opinie na temat sposobu uproszczenia problemu byłoby również mile widziane.

Wielkie dzięki!

0
Nicholas Bock 23 listopad 2013, 02:50

4 odpowiedzi

Najlepsza odpowiedź

Wypróbuj następujące informacje w interpreterie Python:

>>> a = [1,1,1]
>>> b = []
>>> b.append(a)
>>> b.append(a)
>>> b.append(a)
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 2], [1, 1, 2], [1, 1, 2]]

Jest to uproszczona wersja tego, co dzieje się w swoim kodzie. Ale dlaczego się dzieje?

b.append(a) faktycznie nie tworzy kopii a i wypchnięcia go do tablicy w b. Wykonuje referencje do a. To jak zakładka w przeglądarce internetowej: Kiedy otworzysz stronę internetową za pomocą zakładki, spodziewasz się, że zobaczysz stronę internetową, jak to jest teraz, nie tak, jak to było, gdy go znalazłeś. Ale oznacza to również, że jeśli masz wiele zakładek na tej samej stronie, a zmienia się ta strona, zobaczysz zmienioną wersję bez względu na zakładkę, którą śledzisz.

To ta sama historia z temp = a i dla tej sprawy a = [1,1,1]. temp i a są "zakładkami" do określonej tablicy, która dzieje się z trzema. I b W powyższym przykładzie jest zakładka do tablicy ... która zawiera trzy zakładki do tej samej tablicy zawierającej trzy te.

Więc co robisz, jest tworzenie nowej tablicy i kopii w elementach starej tablicy. Najszybszym sposobem, aby to zrobić, ma przyjąć kawałek tablicy zawierający całą tablicę, jak pokazano User2357112:

>>> a = [1,1,1]
>>> b = []
>>> b.append(a[:])
>>> b.append(a[:])
>>> b.append(a[:])
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 2]]

Dużo lepiej.

1
Jander 22 listopad 2013, 23:25
temp = fresh_start

Nie tworzy kopii. Dołączanie nie powoduje kopii, przypisanie nie robi kopii i prawie wszystko, co nie mówi, że kopia nie tworzy kopii. Jeśli chcesz kopii, pokroić to:

fresh_start[:]

To kopia.

3
user2357112 supports Monica 22 listopad 2013, 22:54

Kiedy patrzę na pożądane wyjście, nie mogę pomóc, ale pomyśleć o stosowaniu jednej z funkcji produkcji numpy siatki.

import numpy
first_column, second_column, third_column = numpy.mgrid[1:4,1:4,1:4]
numpy.dstack((first_column.flatten(),second_column.flatten(),third_column.flatten()))
Out[23]: 
array([[[1, 1, 1],
    [1, 1, 2],
    [1, 1, 3],
    [1, 2, 1],
    [1, 2, 2],
    [1, 2, 3],
    [1, 3, 1],
    [1, 3, 2],
    [1, 3, 3],
    [2, 1, 1],
    [2, 1, 2],
    [2, 1, 3],
    [2, 2, 1],
    [2, 2, 2],
    [2, 2, 3],
    [2, 3, 1],
    [2, 3, 2],
    [2, 3, 3],
    [3, 1, 1],
    [3, 1, 2],
    [3, 1, 3],
    [3, 2, 1],
    [3, 2, 2],
    [3, 2, 3],
    [3, 3, 1],
    [3, 3, 2],
    [3, 3, 3]]])

Oczywiście, użyteczność tego konkretnego podejścia może zależeć od różnorodności danych wejściowych, z którymi musisz się poradzić, ale podejrzewam, że może to być interesujący sposób na budowę danych, a Numpy jest dość szybki dla tego rodzaju. Przypuszczalnie, jeśli lista wejściowa ma więcej elementów, możesz mieć więcej MIN: Maksymalne argumenty karmione w Mgrid [], a następnie rozpakować / stos w podobny sposób.

0
treddy 22 listopad 2013, 23:27

Oto uproszczona wersja twojego programu, która działa. Komentarze będą śledzić.

number_list = []

def _adjust_counter_value(counter, n, max_count):
    """
    We want the counter to go from 1 to max_count, then start over at 1.
    This function adds n to the counter and then returns a tuple:
    (new_counter_value, carry_to_next_counter)
    """
    assert max_count >= 1
    assert 1 <= counter <= max_count

    # Counter is in closed range: [1, max_count]
    # Subtract 1 so expected value is in closed range [0, max_count - 1]
    x = counter - 1 + n
    carry, x = divmod(x, max_count)

    # Add 1 so expected value is in closed range [1, max_count]
    counter = x + 1
    return (counter, carry)

def increment_counter(start, i, max_count):
    last = len(start) - 1 - i
    copy = start[:]  # make a copy of the start

    add = 1  # start by adding 1 to index
    for i_cur in range(last, -1, -1):
        copy[i_cur], add = _adjust_counter_value(copy[i_cur], add, max_count)
        if 0 == add:
            return (copy, i, max_count)
    else:
        # if we have a carry out of the 0th position, we are done with the sequence
        return None

def all_values(fresh_start, i, max_count):
    number_list.append(fresh_start)
    new_values = increment_counter(fresh_start,i,max_count)
    if new_values != None:
        all_values(*new_values)

all_values([1,1,1],0,3)

import itertools as it
correct = [list(tup) for tup in it.product(range(1,4), range(1,4), range(1,4))]
assert number_list == correct

Ponieważ chcesz, aby liczniki przejść z 1 przez {x0}} włącznie, jest trochę trudne do zaktualizowania każdego licznika. Twoim oryginalnym rozwiązaniem było użycie instrukcji if, ale tutaj wykonałem funkcję pomocnika, która używa divmod(), aby obliczyć każdą nową cyfrę. Pozwala nam dodać wszelkie zwiększenie do dowolnej cyfry i znajdzie poprawny wykonanie cyfry.

Twój oryginalny program nigdy nie zmienił wartości i, więc mój poprawiony nie. Możesz uprościć program dalej, pozbycie się i i po prostu mając increment_counter() zawsze przejść do ostatniej pozycji.

Jeśli uruchomisz for pętla do końca bez wywołania {x2}} lub return, etui else: zostanie uruchomiony, jeśli jest jeden obecny. Tutaj dodałem futerał else:, aby obsługiwać wykonanie z 0 miejsce na liście. Jeśli istnieje przeprowadzona z 0 miejsce, oznacza to, że osiągnęliśmy koniec sekwencji licznika. W tym przypadku wrócimy None.

Twój oryginalny program jest rodzaj trudnych. Ma dwa wyraźne {x0}} instrukcje w counter() i ukryte powrót na końcu sekwencji. Wraca None, aby zasygnalizować, że rekurencja może się zatrzymać, ale sposób, w jaki to robi jest zbyt trudne dla mojego gustu. Polecam użycie wyraźnego return None, jak pokazałem.

Należy pamiętać, że Python ma moduł itertools, który zawiera sposób na wygenerowanie serialu licznika takiego jak takiego. Użyłem go, aby sprawdzić, czy wynik jest poprawny.

Jestem pewien, że piszę to, aby dowiedzieć się o rekurencji, ale być doradzanym, że Python nie jest najlepszym językiem dla rozwiązań rekurencyjnych takich jak ten. Python ma stosunkowo płytki stos rekurencyjny i nie wyłącza automatycznie odzyskiwania ogona w pętli iteracyjnej, więc może to spowodować przepełnienie stosu wewnątrz Pythona, jeśli Twoje rekurencyjne połączenia wzywa wystarczająco dużo czasu. Najlepszym rozwiązaniem w Pythonie będzie użyć itertools.product(), jak zrobiłem, aby po prostu wygenerować żądaną sekwencję licznika.

Ponieważ wygenerowana sekwencja jest listą list, a {X0}} wytwarza Kto |, użyłem rozumienia listy, aby przekonwertować każdą krotkę do listy, więc wynik końcowy jest listą list i możemy po prostu użyć Pythona { {X1}} Operator, aby je porównać.

0
steveha 22 listopad 2013, 23:50