2010-12-02 6 views
1

J'essaie de générer une liste de nombres premiers en utilisant la méthode this. J'ai besoin de faire une boucle à travers chaque numéro 2 ... n et de le vérifier pour des multiples de 2 ... n. Pour une raison quelconque, la mauvaise liste semble être modifiée.Python modifiant une mauvaise liste?

import sys 
import argparse 
import math 

parser = argparse.ArgumentParser(description='find the largest prime factor of a number') 
parser.add_argument('n', type=int, help='number') 
args = parser.parse_args() 

sieve = [] 
for i in range(2,args.n+1): sieve.append(i) # tried int(i) 


copy1 = sieve # tried making extra copies. . . 
copy2 = sieve 
copy3 = sieve 
#print int(math.sqrt(args.n)) 

for index, i in enumerate(copy1): 
    #print index, i 
    for ii in copy2: 
     #print ii 
     if i % ii == 0: 
      sieve[index]= None 


print sieve 

je reçois l'erreur suivante:

Traceback (most recent call last): 
    File "3.py", line 22, in <module> 
    if i % ii == 0: TypeError: unsupported operand type(s) for %: 
'int' and 'str' 

Répondre

7

Vous n'êtes pas faire des copies. Vous utilisez des références, donc copy1, copy2, et copy3 se réfèrent tous à la même liste - sieve. Si vous souhaitez copier, utiliser:

copy1 = sieve[:] 

qui va créer une copie de sieve et l'affecter à copy1.

+4

Cette confusion est si commune ... tout le matériel sur la langue (et sur toutes les langues avec une sémantique similaire, btw) devrait contenir une énorme boîte rouge avec un résumé de cela. – delnan

+2

delnan: Aussi, un décrivant comment fonctionne l'arithmétique en virgule flottante. :-) – Ken

+0

Et dans Java String == String n'est pas ce que vous voulez faire. – Falmarri

2

Vous devez utiliser

copy1 = sieve[:] # tried making extra copies. . . 
copy2 = sieve[:] 
copy3 = sieve[:] 

à copier en fait la liste. Sinon, il vous suffit de copier la référence à la liste.

>>> a = [1,2] 
>>> b = a 
>>> c = a[:] 
>>> b[0] = 0 
>>> c[0] = 3 
>>> a 
[0, 2] 
>>> b 
[0, 2] 
>>> c 
[3, 2] 
2
copy1 = sieve 
copy2 = sieve 
copy3 = sieve 

Ce ne sont pas des copies, ils sont de référence.

primes = [2,3,5,7] 

def is_prime(n): 
    if n in primes: 
     return True 
    for item in primes: 
     if n % item == 0: 
      return False 
    return True 

assert is_prime(4) == False 
assert is_prime(29) == True 
assert is_prime(65) == False 

est une bonne méthode de tamis

Plus de tests unitaires parce que son plaisir

true_primes = [int(item) for item in '11,13,17,19,23,29,31,37,41,43,47'.split(',')] 
for item in xrange(10, 50): 
    if is_prime(item) == True: 
     assert item in true_primes 
    else: 
     assert item not in true_primes 
1

je ne pouvais pas tester cela, parce que je n'ai pas une copie de Python 3.2 (argparse est nouveau dans Python 3.2), mais deux choses évidentes viennent à l'esprit:

D'abord, vous devez faire sieve.append(int(i)). Deuxièmement, vous ne faites pas de copie de tamis, vous créez simplement une nouvelle référence à la même liste. Pour faire une copie, vous devez

copy1 = sieve[:] 
2

Python a une sémantique de référence. En général, a = b provoque le nom a pour faire référence à la même valeur que le nom b fait actuellement référence. Il ne crée ou ne stocke pas de nouvelle valeur.

Vous pouvez cloner une liste avec l'astuce [:] mentionnée. Une solution plus générale pour copier des choses consiste à utiliser le module copy.

Cependant, un bon code Python ne nécessite normalement pas de copier explicitement des choses très souvent. Vous devez vous familiariser avec l'utilisation de la liste de compréhension pour créer des "versions modifiées" de séquences existantes. Par exemple, nous pouvons mettre en œuvre le tamis comme (montrer quelques autres choses):

import sys, argparse, math, itertools 

parser = argparse.ArgumentParser(description='find the largest prime factor of a number') 
parser.add_argument('n', type=int, help='number') 
args = parser.parse_args() 

# Using a loop over a 'range' to fill a list with increasing values is silly, because 
# the range *is* a list of increasing values - that's how the 'for i in ...' bit works. 
sieve = range(2, args.n + 1) 

# We don't need to remember the original loop at all. 
# Instead, we rely on each iteration of the sieve putting a new prime at the beginning. 
for index in itertools.count(): # Counting upward, 
    if index >= len(sieve): break # until we run out of elements, 
    prime = sieve[index] # we grab the next prime from the list, 
    sieve = [x for x in sieve if x == prime or x % prime != 0] # and sieve the list with it. 
# Of course, we can optimize that by checking that prime < sqrt(args.n), or whatever. 

print sieve 
+0

Il a été porté à mon attention que vous travaillez en Python 3.x, auquel cas 'range' est en fait ce que' xrange' était dans Python 2.x. Pour remédier à cela, nous pouvons juste faire quelque chose comme 'sieve = list (range (2, args.n + 1))', explicitement list-ifying le générateur. –