Klasse zur Primfactor-zerlegung und zum primzahlen errechnen

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

Hio, ich hab ne Klasse für Primfactor-zerlegung und Primzahlen-errechnung
geschrieben, die ich mal publizieren wollte :)

Code: Alles auswählen

#!/usr/bin/python
# Author [Editiert]
# License noch keine aber auf jedenfall GPL kompatibel :)
# Object, das primzahlen generieren, 
# und/oder zahlen in ihre primfactoren zerlegen kann
# keine ahnung was man noch so schreibt, viel Spaß ;)

class Primops(object):
	"""
		handles two prim-operations
	"""
	counter = 0
	def __init__(self, prims = [2]):
        self.prims = prims
		if self.prims[-1] > 2: self._proof = self.prims[-1]  +2
        else: self._proof = 3
	
	def generate(self):
		"""
			tests if _proof is a prim
		"""
		for prim in self.prims:
			if ( self._proof % prim ) == 0:
				self._proof += 2
				return False
			elif ( self._proof / prim ) < prim:
				self.prims.append(self._proof)
				self._proof += 2
				return True

	def loop(self, max = 1):
		"""
			generates new prim's
		"""
		if max == 1:
			while max:
				if self.generate(): print self.prims[-1]
		elif max > self._proof:
			while max > self._proof:
				if self.generate(): print self.prims[-1]

	
	def aktuell(self):
		"""
			returns the aktuell used prim

			is used by split only
		"""
		return self.prims[self.counter]

	def count(self):
		"""
			next prim

			is used by split only
		"""
		self.counter += 1
	
	def cycle(self):
		"""
			begin with the first prime

			is used by split only
		"""
		self.counter = 0

	def split(self, number):
		"""
			splits a number into its factors
			and returns all factors
		"""
		self._number = number
		self.splits = []
		while 1:
			try: self.prim = self.aktuell()
			except:
				self.generate()
				continue
			if ( self._number % self.prim ) == 0:
				self.splits.append(self.prim)
				self._number /= self.prim
				self.cycle()
			elif ( self._number / self.prim ) < self.prim:
				if self._number != 1: self.splits.append(self._number)
				self.cycle()
				return self.splits
			else: self.count()
Zuletzt geändert von Leonidas am Freitag 16. Mai 2014, 12:55, insgesamt 1-mal geändert.
Grund: Name auf verlangen editiert.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Ein paar Anmerkungen dazu:

Die von dir gewählten Bezeichner für die einzelnen Methoden sind überwiegend nicht geglückt. Dass "loop" Primzahlen erzeugt und ausgibt, legt die Bezeichnung nicht nahe. Vielleicht solltest du es mal mit deutschen Bezeichnern versuchen. (Primzahl heißt im engl. übrigens prime)

Hilfreich ist es auch, wenn man aus dem Methodennamen erschließen kann, ob die Methode z.B. Werte ausgibt (so wie loop) oder etwas zurückliefert (so wie split). Also z.B. print_primes() und get_factorlist().

Du solltest die Methoden kennzeichnen nach privaten Methoden und öffentlichen Methoden, indem du die privaten mit einem führenden Unterstrich beginnst. So braucht man nicht lange zu überlegen, ob, wofür und wann man z.B. generate() aufrufen muss (nämlich gar nicht, weil es eine private Methode ist).

Zwar ist es zulässig, nach einem Doppelpunkt in der gleichen Zeile fortzufahren, aber es ist unüblich und trägt nicht zur besseren Lesbarkeit eines Quelltextes bei.

Die Variable counter in Zeile 14 wird nicht verwendet. Wenn du sie außerhalb einer Methode innerhalb der Klasse definierst, ist es eine Klassenmethode, die über den Klassennamen angesprochen wird. Mit der Objektvariablen self.counter hat die nichts zu tun - die wird bei dir erstmals in cycle() definiert.

Methoden, deren Rumpf nur aus einer einzigen Anweisung bestehen, sind in aller Regel überflüssig (es sei denn, man beabsichtigt, noch weitere Funktionalität hinzuzufügen). Also: cycle(), count() und aktuell() können weg.

Die Klammern bei deinen bedingten Anweisungen sind nicht nötig.

Ein allgemeines except ohne Angabe des Ausnahmetyps sollte man vermeiden. In deinem Fall wäre es ein IndexError beim Zugriff auf die Liste.

Statt "while 1:" sollte man "while True:" schreiben. Das liest sich besser.
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

Danke für Verbesserungen, ich habe das Programm so weit wie möglich verändert,
damit es lesbarer wird. Das variablen die außerhalb von Funktionen deklariert werden
ausschließlich über den Klassennamen angesprochen werden können war mir bis jetzt
auch nicht bewusst, erklärt aber einige Fehler in anderen Programmen ^^ thx

hier die überarbeitete version:

Code: Alles auswählen

#!/usr/bin/python
# Author [Editiert]
# License noch keine aber auf jedenfall GPL kompatibel :)
# Object, das primzahlen generieren, 
# und/oder zahlen in ihre primfactoren zerlegen kann
# keine ahnung was man noch so schreibt, viel Spass ;)

class Primops(object):
	"""
		handles two prim-operations
	"""
	def __init__(self, primes = [2]):
		self.primes = primes
		
		if self.primes[-1] > 2:
			self._proof = self.primes[-1] +2
		
		else: 
			self._proof = 3


	def _test(self):
		"""
			tests if _proof is a prim
		"""
		for prime in self.primes:
			
			if self._proof % prime == 0:
				self._proof += 2
				return False
			
			elif self._proof / prime < prime:
				self.primes.append(self._proof)
				self._proof += 2
				return True


	def generate_primes(self, max = 1):
		"""
			generates new primes
		"""
		
		if max == 1:
			while max:
				if self._test(): 
					print self.primes[-1]
		
		elif max > self._proof:
			while max > self._proof:
				if self._test(): 
					print self.primes[-1]


	def get_factors(self, number):
		"""
			splits a number into its factors
			and returns all factors
		"""
		
		self._counter = 0
		self._number = number
		self._splits = []
		
		while True:
			
			try: 
				self._prime = self.primes[self._counter]
			
			except: # index_error in self.primes
				self._test()
				continue
			
			if self._number % self._prime == 0:
				self._splits.append(self._prime)
				self._number /= self._prime
				self._counter = 0
			
			elif self._number / self._prime < self._prime:
				if self._number != 1: 
					self._splits.append(self._number)
				return self._splits
			
			else:
				self._counter += 1
Zuletzt geändert von Leonidas am Freitag 16. Mai 2014, 12:55, insgesamt 1-mal geändert.
Grund: Name auf verlangen editiert.
lunar

Betreffend: Zeile 70

Code: Alles auswählen

except: # index_error in self.primes
"except IndexError:", dann weiß auch der Interpreter, dass hier nur IndexError abgefangen werden soll.

Ob die Primfaktorzerlegung nun ein geeignetes Beispiel für eine Klasse ist, sei mal dahingestellt ...
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

@lunar ahh ok :) ist ja praktisch dass man auch einzelne Fehler abfangen kann.
iss ne Klasse, weil man auf Klassen so schön aufbauen kann, ohne den quältext neu zu schreiben, müsste mal ausprobieren, wie viel schneller der ist wenner "glatt" runtergeschrieben wird. Allgemein bin ich mit der geschwindigkeit aber ganz zufrieden.
Jetzt wird noch eine Funktion hinzugefügt, um einzelne zahlen zu prüfen ;)
lunar

nuss hat geschrieben:@lunar ahh ok :) ist ja praktisch dass man auch einzelne Fehler abfangen kann.
Das steht unter anderem auch im Tutorial ...
iss ne Klasse, weil man auf Klassen so schön aufbauen kann, ohne den quältext neu zu schreiben
Man kann auch Funktionen mehrfach aufrufen ...
müsste mal ausprobieren, wie viel schneller der ist wenner "glatt" runtergeschrieben wird. Allgemein bin ich mit der geschwindigkeit aber ganz zufrieden.
Das hat nichts mit der Ausführungsgeschwindigkeit zu tun, sondern einfach damit, dass eine Primfaktorzerlegung auch wunderbar als Funktion geschrieben werden kann, da braucht es keine Klasse. Im "math"-Modul liegen schließlich auch Funktionen ...
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

hey hey ich meld mich nochmal nach laengerem ;)

hab den ganzen generator nochmal ueberarbeitet und mit funktionen geschrieben.
http://paste.pocoo.org/show/89307/
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Hab jetzt auch mal was probiert: http://paste.pocoo.org/show/89313/. Wenn das Argument eine Primzahl ist, wird True zurückgegeben, sonst wird eine Liste mit den Faktoren zurückgegeben.
Zuletzt geändert von derdon am Dienstag 28. Oktober 2008, 20:57, insgesamt 1-mal geändert.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

nuss hat geschrieben:hab den ganzen generator nochmal ueberarbeitet und mit funktionen geschrieben.
http://paste.pocoo.org/show/89307/
Da kommen mindest 8 `global`s zu viel drin vor.
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

DasIch hat folgendes geschrieben:
nuss hat folgendes geschrieben:
hab den ganzen generator nochmal ueberarbeitet und mit funktionen geschrieben.
http://paste.pocoo.org/show/89307/

Da kommen mindest 8 `global`s zu viel drin vor.
Da kommt genau 8 mal global drin vor ;)
Was iss den so schlecht an global ?
cache und proof sind ja instanzen von dem modul aus dem ich get_factors etc. importiere.
Sprich ich kann in einem anderen programm trotzdem noch
ein cache definieren, ohne mit dem cache aus dem modul in konflikt zu kommen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

nuss hat geschrieben:Da kommt genau 8 mal global drin vor ;)
Was iss den so schlecht an global ?
Es führt zu schlecht verständlichen Spaghetticode der zudem auch noch die tollsten Probleme mit Threading verursachen kann. Es gibt fast keinen Code wo man nicht mit Rückgabewerten arbeiten kann statt der globals. Das es die gibt, vergisst du am besten gleich wieder.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

ok ok überzeugt :cry:
habs angepasst.
http://paste.pocoo.org/show/89398/
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

nuss hat geschrieben:habs angepasst.
So, und nun PEP8 lesen und anwenden ;)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

sooo..... ich hab gerade PEP8 gelesen und den Code angepasst.
Ich hoffe das der jetzt größtenteils kompatibel ist.
Vor allem hoffe ich dass die Docstrings auch für Engländer halbwegs verständlich sind.
http://paste.pocoo.org/show/89415/

Was mir eben noch aufgefallen ist, ist dass die funktionen anscheinend die bereits errechneten primzahlen speichern.... wie kommt das ? :shock:
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

nuss hat geschrieben:Was mir eben noch aufgefallen ist, ist dass die funktionen anscheinend die bereits errechneten primzahlen speichern.... wie kommt das ? :shock:
Das liegt daran, dass die default-Werte für "cache" alle mutable sind. Da diese nur einmal erzeugt werden arbeitest du immer auf der selben liste. Lösen kannst du das, indem du als Parameter "cache=None" verwendest und in der Funktion "if cache ist None: cache=[2]" hinzufügst.

"get_factors" und "isprime" sehen so aus, als könnte man sie in Teilen zusammenfassen.

Deine Einrückung ist ein wenig in Mitleidenschaft gezogen. Du solltest nicht Leerzeichen mit Tabs vermischen.
nuss
User
Beiträge: 53
Registriert: Donnerstag 28. August 2008, 11:36

so hab noch'n paar Sachen verbessert:
http://paste.pocoo.org/show/89434/

@EyDu:
Hab jetzt in der Funktion get_new_primes proof und cache auf None gesetzt.
Die anderen Funktionen können ruhig die errechneten Primzahlen weiterverwenden.


Wiie kriegst du das raus, obs Tabs oder Spaces sind ?
Wenn ich python -tt ausführe, das Modul exportiere,
und alle Funktionen einmal aufrufe, gibt er mir jedenfalls keine Fehler aus.

man könnte die funktionen get_factors und isprime so vereinen:

Code: Alles auswählen

def _prime_ops(number, split, cache=[2]):
    
    proof = _get_first(cache)
    factors = []
    counter = 0

    while True:
        try:
            prime = cache[counter]
        except IndexError:

            cache, proof = get_new_primes(proof+1, proof, cache)
            continue

        if number % prime is 0:
            if split is False:
                return (False, cache)
            else:
        	    factors.append(prime)
        	    number /= prime
        elif number / prime < prime:
            if split is False:
                return (True, cache)
            else:
                if number is not 1:
            	    factors.append(number)
                return (factors, cache)
        else:
        	counter += 1

def get_factors(number, cache=[2]):
    return _prime_ops(number, True, cache)

def isprime(number, cache=[2]):
    return _prime_ops(number, False, cache)
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

Schmerz! Need a Medic!
Du ueberpruefst Werte mit is, das ist ganz ganz boese. "is" prueft Identitaet, "==" den Wert.
PS: Nicht das ich euren Spass verderben will, aber wie waer es damit:

Code: Alles auswählen

def get_primes(max_):
    sieve = [True] * (max_ + 1)
    sieve[0] = sieve[1] = False
    for cur_number in xrange(2, max_ + 1):
        if sieve[cur_number]:
            j = 2 * cur_number
            while j <= max_:
                sieve[j] = False
                j += cur_number
            yield cur_number
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

name hat geschrieben:Schmerz! Need a Medic!
Du ueberpruefst Werte mit is, das ist ganz ganz boese. "is" prueft Identitaet, "==" den Wert.
PS: Nicht das ich euren Spass verderben will, aber wie waer es damit:
Ein Thema: Primzahlen finden.
Anderes Thema: Primfaktoren einer Zahl finden

Das erste ist simpel und dafür hast du eine mögliche Lösung geboten.
Das andere ist alles andere als simple. Dafür hast du keine Lösung geboten und alle anderen hier sind ziemlich "unperformant".
Um das zu sehen, nehme man mal so "einfache" Zahlen wie:
348234823482389479
oder
3482348234823894793453453455


Natürlich kann man darüber streiten, ob Python überhaupt für solche Kategorien numerischer Probleme geeignet ist. Aber mit einem halbwegs vernünftigen Algorithmus lässt sich auch hier etwas rausholen (mal mit den obigen Zahlen testen) ;-)

Code: Alles auswählen

import math
def primtest(z):
        """
        Primfaktorenzerlegung
        (c)2008 Qubit
        hannover.post@gmail.com
        """
        
        primfak=[]
        m = 0
        found = 0
        limit = int((math.sqrt(z)-1)/2)
        while z % 2 == 0:
                z = z/2
                primfak.append((2,z))
                print "found:",primfak[-1]
        i = (z-1)/2
        while (i-1) % 3 == 0:
                i = (i - 1)/3
                primfak.append((3,2*i+1))
                print "found:",primfak[-1]
        if (i-2) % 3 == 0:
                u = (i-2)/3
                s = 2
        if (i-3) % 3 == 0:
                u = (i-3)/3
                s = 3
        while m < u and m < limit:
                if s == 2:
                        if not found and (u-m) % (6*m+5) == 0:
                                n = (u-m) / (6*m+5)
                                m0 = 3*m + 2
                                n0 = 3*n
                                primfak.append((2*m0+1,2*n0+1))
                                print "found:",primfak[-1]
                                found = 1
                        if not found and (u-5*m-5) % (6*m+7) == 0:
                                n = (u-5*m-5) / (6*m+7)
                                m0 = 3*m + 3
                                n0 = 3*n + 2
                                primfak.append((2*m0+1,2*n0+1))
                                print "found:",primfak[-1]
                                found = 1
                if s == 3:
                        if not found and (u-5*m-3) % (6*m+5) == 0:
                                n = (u-5*m-3) / (6*m+5)
                                m0 = 3*m + 2
                                n0 = 3*n + 2
                                primfak.append((2*m0+1,2*n0+1))
                                print "found:",primfak[-1]
                                found = 1
                        if not found and (u-m) % (6*m+7) == 0:
                                n = (u-m) / (6*m+7)
                                m0 = 3*m + 3
                                n0 = 3*n
                                primfak.append((2*m0+1,2*n0+1))
                                print "found:",primfak[-1]
                                found = 1
                if found == 1:
                        i = n0
                        while (i-1) % 3 == 0:
                                i = (i - 1)/3
                                primfak.append((3,2*i+1))
                                print "found:",primfak[-1]
                        if (i-2) % 3 == 0:
                                u = (i-2)/3
                                s = 2
                        if (i-3) % 3 == 0:
                                u = (i-3)/3
                                s = 3
                        limit = int((math.sqrt(2*i+1)-1)/2)
                        found = 0
                        m -= 1
                m += 1

        if len(primfak) > 0:
                x = [x[0] for x in primfak]+[primfak[-1][1]]
                if x[-1] == 1: x.pop()
        else: x=[z]
        print "Primfaktoren: ",sorted(x)
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

Qubit hat geschrieben:

Code: Alles auswählen

....
....
Scheint zu funktionieren, schaut aber eher, naja, nicht sehr uebersichtlich aus. Koenntest du den Algorithmus bitte einfach so, bzw mit Pseudocode erklaeren?
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

name hat geschrieben:
Qubit hat geschrieben:

Code: Alles auswählen

....
....
Scheint zu funktionieren, schaut aber eher, naja, nicht sehr uebersichtlich aus. Koenntest du den Algorithmus bitte einfach so, bzw mit Pseudocode erklaeren?
Nun, die mathematischen Hintergründe halte ich in diesem Forum für "beyond the scope", nur soviel, es ist etwas "Selbstgestricktes" und soll leicht portierbar sein ;-)

Habe es noch mal als Klasse implementiert..

Code: Alles auswählen

import math,time

class primetest(object):
        """
        Primfaktorenzerlegung, Primzahlentest, Primzahlensuche
        (c)2008 Qubit
        hannover.post@gmail.com
        """
        __time = {
                'fak': 0,
                'is_prime': 0,
                'primes': 0
                }

        def __getattr__(self,attr):
                attrs = {
                        't_fak': self.__time['fak'],
                        't_is_prime': self.__time['is_prime'],
                        't_primes': self.__time['primes']
                        }
                get = attrs.get(attr,None)
                if not get == None:
                        return tuple(str('%.3f,sec' %(get)).split(','))
                else:
                        object.__getattribute__(self,attr)
                
        def __fak(self,z,mode):
                primfak=[]
                m = 0
                found = 0
                limit = int((math.sqrt(z)-1)/2)
                while z % 2 == 0:
                        if mode == 1 and not z == 2: return [z,False]
                        z = z/2
                        primfak.append((2,z))
                i = (z-1)/2
                while (i-1) % 3 == 0:
                        if mode == 1 and not i == 1: return [z,False]
                        i = (i - 1)/3
                        primfak.append((3,2*i+1))
                if (i-2) % 3 == 0:
                        u = (i-2)/3
                        s = 2
                if (i-3) % 3 == 0:
                        u = (i-3)/3
                        s = 3
                while m < u and m < limit:
                  if s == 2:
                          if (u-m) % (6*m+5) == 0:
                                n = (u-m) / (6*m+5)
                                m0 = 3*m + 2
                                n0 = 3*n
                                primfak.append((2*m0+1,2*n0+1))
                                found = 1
                          if not found and (u-5*m-5) % (6*m+7) == 0:
                                n = (u-5*m-5) / (6*m+7)
                                m0 = 3*m + 3
                                n0 = 3*n + 2
                                primfak.append((2*m0+1,2*n0+1))
                                found = 1
                  if s == 3:
                          if (u-5*m-3) % (6*m+5) == 0:
                                n = (u-5*m-3) / (6*m+5)
                                m0 = 3*m + 2
                                n0 = 3*n + 2
                                primfak.append((2*m0+1,2*n0+1))
                                found = 1
                          if not found and (u-m) % (6*m+7) == 0:
                                n = (u-m) / (6*m+7)
                                m0 = 3*m + 3
                                n0 = 3*n
                                primfak.append((2*m0+1,2*n0+1))
                                found = 1
                  if found == 1:
                          if mode == 1 and not 2*m0+1 == z:
                                  return [z,False]
                          i = n0
                          while (i-1) % 3 == 0:
                                i = (i - 1)/3
                                primfak.append((3,2*i+1))
                          if (i-2) % 3 == 0:
                                u = (i-2)/3
                                s = 2
                          if (i-3) % 3 == 0:
                                u = (i-3)/3
                                s = 3
                          limit = int((math.sqrt(2*i+1)-1)/2)
                          found = 0
                          m -= 1
                  m += 1

                if len(primfak) > 0:
                        x = [x[0] for x in primfak]+[primfak[-1][1]]
                        if x[-1] == 1: x.pop()
                else: x=[z]
                return x

        def __is_prime(self,z): return len(self.__fak(z,mode=1)) == 1
        def __primes(self,bis,von): return filter(self.__is_prime, range(von,bis+1))

        def fak(self,z=2):
                t_start = time.clock()
                res = self.__fak(z,mode=0)
                t_elapsed = time.clock() - t_start
                self.__time['fak'] = t_elapsed
                return res
        def is_prime(self,z=2):
                t_start = time.clock()
                res = self.__is_prime(z)
                t_elapsed = time.clock() - t_start
                self.__time['is_prime'] = t_elapsed
                return res
        def primes(self,bis=2,von=2):
                t_start = time.clock()
                res = self.__primes(bis,von)
                t_elapsed = time.clock() - t_start
                self.__time['primes'] = t_elapsed
                return res


>>> prim=primetest()
>>> prim.is_prime(348234823482389479)
False
>>> prim.t_is_prime
('0.350', 'sec')
>>> prim.fak(348234823482389479)
[413243, 842687773253L]
>>> prim.t_fak
('2.057', 'sec')
>>> prim.is_prime(123456789)
False
>>> prim.t_is_prime
('0.000', 'sec')
>>> prim.fak(123456789)
[3, 3, 3607, 3803]
>>> prim.t_fak
('0.001', 'sec')
>>> res = prim.primes(10000)
>>> prim.t_primes
('0.142', 'sec')
>>> res[-10:]
[9887, 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973]
>>> len(res)
1229
>>> 
Antworten