Seite 2 von 3

Re: Was ist falsch an meinem code?

Verfasst: Montag 8. Juli 2013, 23:33
von Sirius3
@Gwunderi: Durch Deine for-Schleife hast Du die »and«-Verknüpfung in eine »or«-Verknüpfung umgewandelt.

Re: Was ist falsch an meinem code?

Verfasst: Montag 8. Juli 2013, 23:44
von snafu
Mal (wenig spektakulär) in Java, wobei ich die Optimierung mit der Quadratwurzel rausgelassen habe und stattdessen immer die Primzahlen bis n/2 ermittle:

Code: Alles auswählen

import java.util.*;

public class PrimeCalculator {
    
    public static List<Integer> getPrimes(int limit) {
        List<Integer> primes = new ArrayList<Integer>();
        if (limit < 2) {
            return primes;
        }
        primes.add(2);
        
        for (int i = 3; i <= limit; i += 2) {
            boolean isPrime = true;
            for (int prime : primes) {
                if (i % prime == 0) {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime) {
                primes.add(i);
            }
        }
        return primes;
    }
    
    public static int getLargestPrimeFactor(int n) {
        if (n < 2) {
            throw new IllegalArgumentException("n must be >= 2");
        }
        List<Integer> candidates = getPrimes(n / 2);
        Collections.reverse(candidates);
        
        int result = -1;
        for (int candidate : candidates) {
            if (n % candidate == 0) {
                result = candidate;
                break;
            }
        }
        return result;
    }
    
    public static void main(String[] args) {
        System.out.println(getLargestPrimeFactor(111));
    }
}
EDIT: Meine Implementierung enthält noch Fehler, merk ich gerade...

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 00:10
von jerch
@"Optimierung bis Quadratwurzel"
Wenn man direkt über die schon gefundenen Teiler geht, ist ja die Wurzel nur die obere Suchgrenze, welche mit jedem Teiler nach unten wandert, da man mit dem Quotienten weiterrechnen kann. Hier bietet sich auch die nochmalige Prüfung des Teilers an, um den Exponenten gleich mit zu finden.

@snafu:
An dem Sieb lässt sich aber noch einiges optimieren ;)

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 00:24
von snafu
@jerch: Das mit der oberen Suchgrenze verstehe ich nicht. Es wurde ja schon angesprochen, dass es desöfteren Situationen gibt, wo der größte Primfaktor *größer* als die Quadratwurzel ist (z.B. bei 77). Wie würde man da im Detail vorgehen?
jerch hat geschrieben:@snafu:
An dem Sieb lässt sich aber noch einiges optimieren ;)
Jepp. Auf meinem Rechner sorgt schon die Ausführung mit größeren 6-stelligen Zahlen eine spürbare Wartezeit.

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 00:28
von Sirius3
@snafu: die ganze Suche nach Primzahlen ist völlig überflüssig (siehe meinen Beitrag oben).
Hier mal mit einem duzend Assemblerbefehlen zur Demonstration:

Code: Alles auswählen

# input %rax = number to factor
# output %rax = largest prime factor
primfac:
        movq $2, %rcx
        jmp _start
_loop:
        xorq    %rdx, %rdx
        divq    %rcx
        orq     %rdx, %rdx
        jne     _cont
_start:
        cvtsi2sdq       %rax, %xmm0
        sqrtsd  %xmm0, %xmm0
        cvttsd2siq      %xmm0, %rdi
        movq    %rax, %rsi
        jmp     _test
_cont:
        incq    %rcx
        movq    %rsi, %rax
_test:
        cmpq    %rdi, %rcx
        jle     _loop
        ret

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 02:03
von snafu
Nochmal Java. Diesmal habe ich mich an Fermat orientiert und hoffentlich nichts übersehen.

Code: Alles auswählen

public class PrimeCalculator {
    
    public static int getLargestPrimeFactor(int n) {
        while (n % 2 == 0) {
            n /= 2;
        }
        int result = 2;
        for (int i = (int) Math.sqrt(n); i < n; i++) {
            double j = Math.sqrt(i * i - n);
            if (j == (int) j) {
                // Quadratzahl gefunden
                result = i + (int) j;
                break;
            }
        }
        return result;
    }
    
    public static void main(String[] args) {
        System.out.println(getLargestPrimeFactor(486847));
    }
}

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 06:25
von Sirius3
@snafu: nicht ganz. Was passiert, wenn der größte Primfaktor nicht größer als die Wurzel von n ist?

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 08:20
von jerch
snafu hat geschrieben:@jerch: Das mit der oberen Suchgrenze verstehe ich nicht. Es wurde ja schon angesprochen, dass es desöfteren Situationen gibt, wo der größte Primfaktor *größer* als die Quadratwurzel ist (z.B. bei 77). Wie würde man da im Detail vorgehen?
Da es maximal einen Primfaktor größer sqrt(n) geben kann, kannst Du diesen ganz leicht ausrechnen: p_max = n / (p_1^a * p_2^b ...)
Wenn Du mit dem Quotienten weiterrechnest, erhältst Du ihn quasi automatisch (Quotient wird entweder 1, dh. Du hast alle unterhalb gefunden, oder eben dieser Faktor).

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 08:34
von BlackJack
Von mir noch mal eine Lösung mit Primzahlen erzeugen in JavaScript, als Kontrast zum Pascal-Programm mit der selben Grundidee:

Code: Alles auswählen

#!/usr/bin/env node
'use strict';
var _ = require('underscore');

var createPrimeGenerator = function () {
    var primes = [2];
    var candidate = 1;
    var testCandidate = function (prime) { return candidate % prime != 0; };
    var next = function () {
        next = function () {
            while (true) {
                candidate += 2;
                if (_(primes).every(testCandidate)) {
                    primes.push(candidate);
                    return candidate;
                }
            }
        };
        return 2;
    };
    return function () { return next(); };
}

var factor = function (n) {
    var result = [];
    var getNextPrime = createPrimeGenerator();
    while (n > 1) {
        var prime = getNextPrime();
        if (n % prime == 0) {
            result.push(prime);
            while (n > 1 && n % prime == 0) n /= prime;
        }
    }
    return result;
}

var main = function () {
    console.log(factor(parseInt(process.argv[2])));
};

main();
@Sirius3: Ich glaube den ersten Befehl nach dem `_loop`-Label kann man weglassen. `%rdx` wird von der Division danach doch sowieso gleich wieder mit einem anderen Wert überschrieben und die Eingaben für ``divq`` müssten in diesem Fall doch nur `%rax` und `%rcx` sein, oder?

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 08:47
von jerch
Den generativen Ansatz kann man noch in der Laufzeit verbessern, wenn man auf 2*3 oder 2*3*5 aligned Pattern geht. Quasi Atkin-light :D

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 09:18
von Gwunderi
Sirius3 hat geschrieben:Durch Deine for-Schleife hast Du die »and«-Verknüpfung in eine »or«-Verknüpfung umgewandelt.
Ach so, und da alle anderen geprüften Zahlen keine Teiler von 486'847 sind, wird sie ausgegeben. Hatte ja schon gesehen, dass die Lösungen gleich mehrfach ausgegeben werden, dachte mir aber, es sei weil jetzt jede Zahl einzeln geprüft wird, was ja auch stimmt - aber dass die "and"-Verknüpufng damit auch verlorengeht ... wäre eigentlich logisch. (Und da es dieselben Ergebnisse lieferte, habe ich gar nicht weiter überlegt.)

Tut mir leid, wenn ich da verschlimmbessere, aber das kleine Genie Eliazz wird sich wohl nicht so leicht verwirren lassen.

Gibt es denn auch eine Möglichkeit, bei einer Array, deren Elemente mit "and" geprüft werden, das in nur einem Schritt zu tun?

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 09:28
von Sirius3
@BlackJack: »div« teilt das 128bit-Register rdx:rax durch rcx. Siehe auch http://download.intel.com/products/proc ... 253666.pdf Seite 293.
Daher müssen die oberen 64bit null sein. Das Ergebnis liegt dann in rax und der Rest der Division in rdx.

@Gwunderi:

Code: Alles auswählen

if all(a%x>0 for x in prime_proof): ...

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 11:06
von Gwunderi
Danke für den Code Sirius

Dann geht es doch so (hoffe ich wenigstens, bei den geprüften Zahlen stimmt es mal):
Da man in "primteiler" eine Anfangszahl eingeben muss, habe ich 2 eingesetzt, daher muss man bei einer ungeraden Zahl daran denken, die 2 aus den Lösungen zu streichen.
Nur geht es bei hohen Zahlen (z.B. der von Eliazz) eine halbe Ewigkeit.
Sollte man also noch eine Methode einbauen, dass man bei jeder neu gefundenen Primzahl die Vielfachen davon vor dem neuen Prüfgang aussiebt (wahrscheinlich ist es das, was einige User hier gemeint haben?)

Code: Alles auswählen

primteiler = [2]

n = int(input("Gib eine ganze Zahl ein: "))

a = 3
        
while a <= n/2:
    if all(a%x > 0 for x in primteiler) and n%a == 0:
            primteiler.append(a)
    a = a + 2
print(primteiler)
Habe es mit folgenden Zahlen ausprobiert:
15015 (=3*5*7*11*13)
10010 (=2*5*7*11*13)
60030 (=2*3*5*23*29)
6040 (gibt 2, 5, 151 heraus)

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 12:22
von snafu
Ich poste jetzt auch einfach mal eine mögliche Python-Lösung, diesmal orientiert an Euklid (was ja eigentlich auch das naheliegendste ist):

Code: Alles auswählen

from itertools import count, takewhile

def get_largest_prime_factor(n):
    if n < 2:
        raise ValueError('n must be >= 2')
    for candidate in takewhile(lambda i: i <= n, count(2)):
        while n % candidate == 0:
            n /= candidate
    return candidate

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 12:44
von Hirschin
Gwunderi hat geschrieben:Hallo zusammen,

Habe auch ein paar Anmerkungen:
Will man prüfen, OB eine Zahl n eine Primzahl ist, genügt es, bis zur Wurzel von n zu prüfen.
Aber um die grösste Primzahl, die Teiler von n ist herauszufinden genügt es nicht, habe ich eben herausgefunden.
Nehmen wir z.B. 33 - ist 33 eine Primzahl? da genügt es, bis 6 zu prüfen, denn 11 * 3 wurde schon bei der 3 geprüft.
Aber die grösste Primzahl, die Teiler von 33 ist - also 11 - würde man hier glatt "übersehen".

Man muss also doch, wie BlackJack anfangs meinte, bis n/2 prüfen!
]

Jaja, lang lang ist es her mit den Primzahlen!
Mit Hilfe der Primfaktorzerlegung wird eine natürliche Zahl in ein Produkt von Primzahlen zerlegt, zum Beispiel ist 24 = 2*2*2*3. Ein Primfaktor muss also zwei Bedingungen erfüllen: Erstens muss ein Primfaktor eine Primzahl sein. 4 kann also kein Primfaktor sein, da sie sich weiter in 2*2 zerlegen lässt. Zweitens muss ein Primfaktor ein Teiler dieser natürlichen Zahl sein. Die Zahl muss also ohne Rest durch den Primfaktor teilbar sein.
(Quelle:http://primfaktorzerlegung.net/)

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 12:47
von Sirius3
die etwas schnellere Variante:

Code: Alles auswählen

from itertools import count, takewhile, chain

def get_largest_prime_factor(n):
    if n < 2:
        raise ValueError('n must be >= 2')
    sqn=n**0.5
    for candidate in takewhile(lambda i: i <= sqn, chain((2,), count(3, 2))):
        while n % candidate == 0 and n>candidate:
            n /= candidate
            sqn=n**0.5
    return n

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 18:30
von snafu
@Sirius3: Jepp, deine Version gibt besonders bei großen Primzahlen Vollgas - zumindest im Gegensatz zu meiner Variante. Danke fürs Posten. :)

Aber das zweite `sqn=n**0.5` macht innerhalb der `while`-Schleife wohl wenig Sinn. Hat zwar keine Riesen-Auswirkungen, aber es sollte IMHO an die Stelle *nach* der `while`-Schleife gesetzt werden. Also quasi kurz bevor die `for`-Schleife ihren nächsten Iterationsschritt macht.

Außerdem verstehe ich nicht, wieso das zusätzliche `and` nötig ist. Wenn man `candidate` zurückliefert, müsste es doch auch ohne den `and`-Teil korrekt funktionieren, oder nicht?

Insgesamt sähe es dann so aus:

Code: Alles auswählen

from itertools import chain, count, takewhile

def get_largest_prime_factor(n):
    if n < 2:
        raise ValueError('n must be >= 2')
    sqn = n**.5
    candidates = chain([2], count(3, 2))
    for candidate in takewhile(lambda i: i <= sqn, candidates):
        while n % candidate == 0:
            n /= candidate
        sqn = n**.5
    return candidate
EDIT: Ich merke gerade, dass man wohl doch nicht einfach so den `candidate` als Rückgabe nehmen kann... :(

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 20:48
von jerch
@snafu:
Die Wurzel innerhalb der Schleife ist wichtig. Ausserhalb würdest Du für jeden Kandidaten das Suchfenster verkleinern, egal ob Teiler oder nicht.
Die UND-Bedingung ist da nötig, da ansonsten n auf eins fallen würde, da der letzte verbleibende Faktor modulo sich selber ja null ergibt und im Schleifenkörper dann durch sich selbst geteilt würde.
Den letzten Kandidaten kannst Du nicht einfach zurückgeben, der wäre nur für eine Quadratzahl richtig. Das "verbliebene" n ist der letzte Teiler.

Eine gegen 2*3 ausgerichtete Version ist etwa doppelt so schnell, dafür aber der Code deutlich länger.

Hier mal die komplette Faktorisierung (mit while, da doch einiges schneller):

Code: Alles auswählen

from math import sqrt

def prime_factors(n):
    sq_n = sqrt(n)
    counter = 0
    candidate = 2
    while candidate <= sq_n:
        while not n % candidate:
            n /= candidate
            sq_n = sqrt(n)
            counter += 1
        if counter:
            yield (candidate, counter)
            counter = 0
        if candidate == 2:
            candidate += 1
        else:
            candidate += 2
    if n > 1:
        yield (n, 1)

def pprint_factors(factors):
  return ' * '.join('%d^%d' % (b, e) if e > 1 else '%d' % (b,)
                    for b, e in factors)
Edit: Seh grad, dass ich die 2 vergessen habe :oops: Fixed

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 21:03
von snafu
@jerch: Ist das so gewollt, dass deine Funktion teilweise Faktoren liefert, die nicht-prim sind?

EDIT: Deine editierte Variante liefert für 9 immer noch (9, 1).

@Meine Codeanmerkungen an Sirius3: Ja, da hatte ich wohl Tomaten auf den Augen, um nicht zu sehen, dass das Wurzelziehen abhängig von einer sich verändernden Größe ist... :oops:

Re: Was ist falsch an meinem code?

Verfasst: Dienstag 9. Juli 2013, 21:11
von jerch
@snafu: Na klar, war das Absicht, wollt testen, ob Du's merkst ;) Da fehlte ein '='