Größtes Palindrom Produkt aus zwei dreistelligen Zahlen

DrPils

Bekanntes Mitglied
Moin,

Ich hänge gerade an folgender Aufgabe:

A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.

Find the largest palindrome made from the product of two 3-digit numbers.


Java:
    public static boolean isPalindrome(int number) {
        int[] digitArray = splitNumberInDigitArray(number);
        for (int i = 0, j = digitArray.length - 1; i < digitArray.length / 2; i++, j--) {
            if (digitArray[i] != digitArray[j]) return false;
        }
        return true;
    }

    public static int[] splitNumberInDigitArray(int number) {
      return   Integer.toString(number)
                .chars()
                .map(c-> Character.digit(c, 10))
                .toArray();
    }

    private static int problem4() {
        for (int i = 999; i > 99; i--) {
            for (int j = 999; j >= i; j--) {
                int product = i * j;
                if (Mathematics.isPalindrome(product)) return product;
            }
        }
        throw new RuntimeException();
    }

Also isPalindrome und splitNumberInDigitArray, passen diese bestehen die Tests.

Wenn ich problem4() mit 99 Teste, bekomme ich auch das Ergebniss aus der Beschreibung.
Mit 999 bekomme ich 888888 was das Produkt aus 924 und 962 ist.
Die Lösung wird aber nicht akzeptiert.
Ich nehme an es liegt irgendwie an der Reihenfolge wie ich prüfe:

Code:
999 * 999
998 * 999
998 * 998
997 * 999
997 * 998
997 * 997
996 * 999
996 * 998
996 * 997
996 * 996
995 * 999
995 * 998
995 * 997
995 * 996
995 * 995
...

Mir leuchtet aber eigentlich nicht ein was daran falsch sein sollte.
 

DrPils

Bekanntes Mitglied
Ok der Fehler ist wohl 923 * 999 ist grösser als 924 * 962, habe das aber noch nicht auf Palindrom geprüft.

Wie könnte man da das nach der Größe des Produktes durchgehen?

Habe jetzt mal einfach alle Palindrom produkte von 999 * 999 - 100 * 100 in eine Liste gelegt und das größte davon genommen. Funktioniert, ist aber mist
 
Zuletzt bearbeitet:

LimDul

Top Contributor
So eine richtig schlaue Idee hab ich nicht, weil es im Endeffekt darum geht die Schleifen für i und j zu optimieren. Und das wiederum ist Frage, wann ist ein Produkt a*b kleiner oder größer als ein Produkt c*d.

Die einzige Ad-Hoc Optimierung die mir einfällt, wenn man rausgefunden hat, dass z.B. 924*962 ein Palindrom ist, braucht man alle Produkte mit i und j kleiner als 924 nicht mehr testen. Denn das Produkt ist auf jeden kleiner als das bereits gefunden. Aber ob 923*963 kleiner oder größer als das Produkt ist, da fällt mir jetzt keine schlaue Bedingung für ein.
 

KonradN

Super-Moderator
Mitarbeiter
Also generell kann man alles etwas einschränken:

1) das a * b = b * a ist, reicht es, alle Produkte zu prüfen für die gilt a >= b. Du startest also mit 999*999, 999*998, ... und Du startest immer mit b = a.

2) Sobald Du ein Palindrom gefunden hast, kannst Du damit Grenzen aufstellen:
Du hast 999 * 923 als Palindrom gefunden. Also kannst Du bei 999 aufhören, denn alle weiteren Palindrome mit 999 vorne wären ja kleiner.
Wenn Du nun 998 testest, dann hast Du als Grenze für die zweite Zahl: 999 * 923 / 998. Das musst Du aber nicht ausrechnen. Du kennst ja das Palindrom und wenn a *b < Palindrom ist, dann kannst Du die Schleife für b abbrechen.

Ich denke, dass Du damit schon sehr viele Versuche gar nicht erst betrachten musst.
 

DrPils

Bekanntes Mitglied
Also generell kann man alles etwas einschränken:

1) das a * b = b * a ist, reicht es, alle Produkte zu prüfen für die gilt a >= b. Du startest also mit 999*999, 999*998, ... und Du startest immer mit b = a.

2) Sobald Du ein Palindrom gefunden hast, kannst Du damit Grenzen aufstellen:
Du hast 999 * 923 als Palindrom gefunden. Also kannst Du bei 999 aufhören, denn alle weiteren Palindrome mit 999 vorne wären ja kleiner.
Wenn Du nun 998 testest, dann hast Du als Grenze für die zweite Zahl: 999 * 923 / 998. Das musst Du aber nicht ausrechnen. Du kennst ja das Palindrom und wenn a *b < Palindrom ist, dann kannst Du die Schleife für b abbrechen.

Ich denke, dass Du damit schon sehr viele Versuche gar nicht erst betrachten musst.

Dankeschön
Habe das mal so umgesetzt, denke ich habe dich richtig verstanden. Ist jetzt auch sehr schnell durch der Algorithmus, leserlich ist zwar anders....

Java:
    private static int problem4() {
        int lastPalindrome = 0;
        for (int i = 999; i > 99; i--) {
            for (int j = i; i >= j; j--) {
                int product = i * j;
                if (product < lastPalindrome) break;
                if (Mathematics.isPalindrome(product)) {
                    lastPalindrome = product;
                    break;
                }
            }
        }
        return lastPalindrome;
    }
 

httpdigest

Top Contributor
Man kann das ganze Problem auch von der andere Seite aus lösen: Also nicht zwei Nummern finden, die zusammenmultipliziert ein Palindrom ergeben, sondern: Palindrome konstruieren und den größten dreistelligen Teiler finden:
Java:
public class Palindrome {
    private static int reverseInt(int v) {
        int r, res = 0;
        while (v > 0) {
            r = v % 10;
            res = res * 10+ r;
            v /= 10;
        }
        return res;
    }
    private static int build3x3Palindrome(int num) {
        return num * 1000 + reverseInt(num);
    }
    private static int findFirstDivisor(int palindrome, int v) {
        for (int i = 999; i >= 100 && i > v; i--)
            if (palindrome % i == 0)
                return i;
        return -1;
    }
    public static int findLargest3x3Palindrome() {
        for (int v = 999; v > 0; v--) {
            int p = build3x3Palindrome(v);
            int d = findFirstDivisor(p, v);
            if (d != -1)
                return p;
        }
        return -1;
    }
    public static void main(String[] args) {
        System.out.println(findLargest3x3Palindrome());
    }
}
 

httpdigest

Top Contributor
Und eine sehr große Zeit geht durch deinen eher suboptimalen Test verloren, ob die Zahl ein Parlindrom ist. Gerade, weil das ja der Teil ist, der am häufigsten ausgeführt wird, weil es direkt in der innersten Schleife ist, solltest du hier noch optimieren. Hier solltest du eher nicht etwas mit Streams und nach Array konvertieren machen, sondern einfach eine Methode schreiben, die einen Integer "umdreht" und dann mittels == prüfen, ob der umgedrehte Integer gleich dem Original ist.
Z.B. so:
Java:
private static int reverseInt(int v) {
    int r, res = 0;
    while (v > 0) {
        r = v % 10;
        res = res * 10 + r;
        v /= 10;
    }
    return res;
}
public static boolean isPalindrome(int number) {
    return number == reverseInt(number);
}
 

Arrive5784

Aktives Mitglied
Ok der Fehler ist wohl 923 * 999 ist grösser als 924 * 962, habe das aber noch nicht auf Palindrom geprüft.
Ich komme da auf (99, 91) und (993, 913). Btw., man ändert immer zuerst die zweite Variable. :

Java:
import java.util.Arrays;

public class PalindromeProduct {
    public int[] find(int digits) {
        int fence = 0;
        int r1 = -1, r2 = -1;
        int a = (int) Math.pow(10, digits) - 1;
        int b = (int) Math.pow(10, digits) - 1;
        for (int i = a; i > 0; i--) {
            for (int j = b; j > 0; j--) {
                int p = i * j;
                if (p > fence) {
                    if (palindrome(p)) {
                        fence = p;
                        r1 = i;
                        r2 = j;
                    }
                } else {
                    break;
                }
            }
        }
        return new int[] {r1, r2};
    }

    public boolean palindrome(int p) {
        String s = String.valueOf(p);
        for (int i = 0; i < s.length() / 2; i++) {
            if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        PalindromeProduct tester = new PalindromeProduct();
        System.out.println(Arrays.toString(tester.find(2)));
        System.out.println(Arrays.toString(tester.find(3)));
    }
}
 

LimDul

Top Contributor
Spannend wäre - aber das sprengt den Scope der Aufgabe - sich mal mathematisch zu überlegen, ob es da Regeln gibt, wann ein Produkt ein Palindrom ist. Ich mein, die Zahl muss ja ein Form wie diese n=abccba, Sprich es gilt:
n=a + 10*b + 100*c + 1000*c + 10000*b+100000*a = a*100001 + b*10010 + c*1100

Keine Ahnung ob man daraus was bzgl. Teiler & Co ableiten kann.
 

Arrive5784

Aktives Mitglied
Da:

Java:
import java.math.BigInteger;
import java.util.Arrays;

public class PalindromeProduct {
    public int[] find(int digits) {
        BigInteger fence = BigInteger.ZERO;
        int[] r = {-1, -1};
        int a = (int) Math.pow(10, digits) - 1;
        System.out.println("a = " + a);
        for (int i = a; i > 0; i--) {
            BigInteger b = BigInteger.valueOf(i);
            if (b.multiply(b).compareTo(fence) <= 0) {
                break;
            }
            for (int j = i; j > 0; j--) {
                BigInteger p = b.multiply(BigInteger.valueOf(j));
                if (p.compareTo(fence) > 0) {
                    if (palindrome(p)) {
                        fence = p;
                        r[0] = i;
                        r[1] = j;
                    }
                } else {
                    break;
                }
            }
        }
        return r;
    }

    public boolean palindrome(BigInteger p) {
        String s = String.valueOf(p);
        for (int i = 0; i < s.length() / 2; i++) {
            if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        PalindromeProduct tester = new PalindromeProduct();
        System.out.println(Arrays.toString(tester.find(2)));
        System.out.println(Arrays.toString(tester.find(3)));
        System.out.println(Arrays.toString(tester.find(4)));
        System.out.println(Arrays.toString(tester.find(5)));
        System.out.println(Arrays.toString(tester.find(6)));
        System.out.println(Arrays.toString(tester.find(7)));
        System.out.println(Arrays.toString(tester.find(8)));
    }
}

Code:
a = 99
[99, 91]
a = 999
[993, 913]
a = 9999
[9999, 9901]
a = 99999
[99979, 99681]
a = 999999
[999999, 999001]
a = 9999999
[9998017, 9997647]
a = 99999999
[99999999, 99990001]

Kann es denn wirklich sein, dass jedes zweite Produktepaar der Gestalt (99999..9, 9990..01) ist? Das ist ja fast trivial.^^

BTW: Ich habe den Code jetzt weitestgehend optimiert. Aber er ist dennoch wegen dem Einsatz von BigInteger langsam. Für digits > 8 müsste man sich was anderes überlegen.
 

DrPils

Bekanntes Mitglied
Ich komme da auf (99, 91) und (993, 913). Btw., man ändert immer zuerst die zweite Variable. :

Java:
import java.util.Arrays;

public class PalindromeProduct {
    public int[] find(int digits) {
        int fence = 0;
        int r1 = -1, r2 = -1;
        int a = (int) Math.pow(10, digits) - 1;
        int b = (int) Math.pow(10, digits) - 1;
        for (int i = a; i > 0; i--) {
            for (int j = b; j > 0; j--) {
                int p = i * j;
                if (p > fence) {
                    if (palindrome(p)) {
                        fence = p;
                        r1 = i;
                        r2 = j;
                    }
                } else {
                    break;
                }
            }
        }
        return new int[] {r1, r2};
    }

    public boolean palindrome(int p) {
        String s = String.valueOf(p);
        for (int i = 0; i < s.length() / 2; i++) {
            if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        PalindromeProduct tester = new PalindromeProduct();
        System.out.println(Arrays.toString(tester.find(2)));
        System.out.println(Arrays.toString(tester.find(3)));
    }
}
Wo ist eigentlich der Unterschied zu meiner implementierung?
 

MarvinsDepression

Bekanntes Mitglied
Der Spoiler von
Man kann das ganze Problem auch von der andere Seite aus lösen: Also nicht zwei Nummern finden, die zusammenmultipliziert ein Palindrom ergeben, sondern: Palindrome konstruieren und den größten dreistelligen Teiler finden:
hat mich dann doch gejuckt das selbst zusammen zu zimmern. Ging mir dann doch nicht so leicht von der Hand, ermöglicht nun dafür einen direkten Laufzeitvergleich der unterschiedlichen Herangehensweisen. Die MainKlasse mit dem ursprünglichen LösungsAnsatz. Die Other...Klasse mit der angedeuteten schnelleren Methode.
Trotz verschiedener Optimierungsversuche (MainKlasse: checkPalindrome1..4() ) ist der Laufzeitunterschied eklantant. Die Other...Klasse benötigt gerade mal 2% der gesamten Programmlaufzeit.


ursprünglichen LösungsAnsatz
Java:
public class TestOmania {
    
    private static final int MAX_LENGTH = 9;
    private final int length, maxValue, minValue;
    
    
    public TestOmania(int l) {
        length = l;
        maxValue = (int)(Math.pow(10, length) - 0.5);
        minValue = maxValue / 10 + 1;
    }
    
    
    public NumberTripple solve() {
        var tripple = new NumberTripple();
        long product;
        
        for (int x = maxValue; x >= minValue; x--) {
            for(int y = x; y >= minValue; y--) {
                product = (long)x * y;
                if (product < tripple.product) break;
                if (checkSymmetry4(product)) {
                    tripple = new NumberTripple(product, x, y);
                    break;
                }
            }
        }
        return tripple;
    }
    
    private boolean checkSymmetry1(long value) {
        char[] number = Long.toString(value).toCharArray();
        
        for (int i = 0; i < number.length / 2; i++) {
            if (number[i] != number[number.length - 1 - i]) return false;
        }
        return true;
    }
    
    private boolean checkSymmetry2(long value) {
        final int[] number = new int[length * 2];
        int len = 0;
        while (value > 0) {
            number[len++] = (int)(value % 10);
            value /= 10;
        }
        for (int i = 0; i < len / 2; i++) {
            if (number[i] != number[len - 1 - i]) return false;
        }
        return true;
    }
    
    private boolean checkSymmetry3(final long value) {
        long copy = value;
        long eulav = 0;
        
        while(copy > 0) {
            eulav *= 10;
            eulav += copy % 10;
            copy /= 10;
        }
        return value == eulav;
    }
    
    private boolean checkSymmetry4(long value) {
        if (value < 10) return true;
        
        int eulav = 0;
        for (int i = 0; i < length; i++) {
            eulav *= 10;
            eulav += value % 10;
            value /= 10;
        }
        return value == eulav;
    }
    
    private static void printValues(int length, NumberTripple values) {
        System.out.println("Length = "+ length + ": "
                    + values.factorA +" * "
                    + values.factorB +" = "
                    + values.product);
    }
    
    public static void main(String[] args) {
        for (int i = 1; i <= MAX_LENGTH; i++) {
            printValues(i, new TestOmania(i).solve());
            printValues(i, new OtherTestOmania(i).solve());
            System.out.println();
        }
    }
}
hier können vier verschiedene checkSymmetry-Methoden durchprobiert werden. Die ersten beiden sind gleich schnell, aber auch die langsamsten.
Die dritte benötigt schon 25% weniger Zeit, die vierte spart (durch Halbierung der benötigten Anzahl an Divisionen) insgesamt 55%. Aber eben alles Peanuts im Vergleich zur folgenden Klasse.

hier beginnt der Spoilerbereich
Code:
public class OtherTestOmania {
    
    private final int length, maxValue, minValue;
    
    
    public OtherTestOmania(int l) {
        length = l;
        maxValue = (int)(Math.pow(10, length) - 0.5);
        minValue = maxValue / 10 + 1;
    }
    
    
    public NumberTripple solve() {
        for (int i = maxValue; i >= minValue; i--) {
            long product = buildPalindrome(i);
            
            for (int x = maxValue; x >= minValue; x--) {
                long y = product / x;
                if (y > x) break;
                if (y >= minValue && x * y == product)
                    return new NumberTripple(product, x, (int)y);
            }
        }
        return new NumberTripple();
    }
    
    private long buildPalindrome(int leftPart) {
        if (leftPart < 10) return (long)leftPart;
        
        long palindrome = (long)leftPart;
        
        while (leftPart > 0) {
            palindrome *= 10;
            palindrome += leftPart % 10;
            leftPart /= 10;
        }
        return palindrome;
    }
}

kleine Hilfsklasse
Code:
package homebrew.testomania;

public class NumberTripple {
    public final long product;
    public final int factorA, factorB;

    public NumberTripple(long p, int a, int b) {
        product = p;
        factorA = a;
        factorB = b;
    }

    public NumberTripple() {
        this(0, 0, 0);
    }
}

für mich wars interessant ... ;)
 

MarvinsDepression

Bekanntes Mitglied
Seh gerade. Die Konsolenausgabe wollte ich auch rein nehmen.
Java:
-------------------< homebrew.testomania:testOmania >-------------------
Building testOmania 1.0-SNAPSHOT
--------------------------------[ jar ]---------------------------------

--- exec-maven-plugin:3.0.0:exec (default-cli) @ testOmania ---
Length = 1: 3 * 3 = 9
Length = 1: 9 * 1 = 9

Length = 2: 99 * 91 = 9009
Length = 2: 99 * 91 = 9009

Length = 3: 993 * 913 = 906609
Length = 3: 993 * 913 = 906609

Length = 4: 9999 * 9901 = 99000099
Length = 4: 9999 * 9901 = 99000099

Length = 5: 99979 * 99681 = 9966006699
Length = 5: 99979 * 99681 = 9966006699

Length = 6: 999999 * 999001 = 999000000999
Length = 6: 999999 * 999001 = 999000000999

Length = 7: 9998017 * 9997647 = 99956644665999
Length = 7: 9998017 * 9997647 = 99956644665999

Length = 8: 99999999 * 99990001 = 9999000000009999
Length = 8: 99999999 * 99990001 = 9999000000009999

Length = 9: 999980347 * 999920317 = 999900665566009999
Length = 9: 999980347 * 999920317 = 999900665566009999

------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time:  06:17 min
Finished at: 2022-05-24T23:11:48+02:00
------------------------------------------------------------------------

Mein PC hat jetzt auch schon ein paar Jahre... Ist ein i5 4. Gen. Hat mal jemand einen Zeitvergleich mit ner halbwegs aktuellen CPU?
 

MarvinsDepression

Bekanntes Mitglied
Stimmt, war nicht dabei.
Java:
    private static void printValues(long time, int length, NumberTripple values) {
        time = System.currentTimeMillis() - time;
        System.out.println(time +"ms  ,"
                +"Length = "+ length + ": "
                + values.factorA +" * "
                + values.factorB +" = "
                + values.product);
    }
    
    public static void main(String[] args) {
        long time;
        for (int i = 1; i <= MAX_LENGTH; i++) {
            time = System.currentTimeMillis();
            printValues(time, i, new TestOmania(i).solve());
            time = System.currentTimeMillis();
            printValues(time, i, new OtherTestOmania(i).solve());
            System.out.println();
        }
    }
Die 2. Methode lediglich 25s benötigt.
 

Arrive5784

Aktives Mitglied
Also, du musst schon genau angeben, welche zwei Methoden du genau vergleichen möchtest. :)

und hier stimmt was nicht:

time = System.currentTimeMillis();
printValues(time, i, new TestOmania(i).solve());
time = System.currentTimeMillis();

die erste Zuweisung an time ist sinnlos, weil sie von der zweiten überschrieben wird...

Eine Zeitmessung geht für Gewöhnlich so:

long t0 = System...;
methodCall();
long t1 = System...;
sout (t1 - t0);
 

Arrive5784

Aktives Mitglied
Die Aussage ist nicht falsch, sie ist aber vielleicht etwas zu streng formuliert: Für Gewöhnlich macht man die Zeitmessung anders, also nicht so. Demzufolge ist das

time = System.currentTimeMillis();
somethingWith(time );
time = System.currentTimeMillis();

schlicht falsch.

Ich befürchte fast, dass, wenn ich die Zeitmessung nochmal mit meiner Methode aus #14 machen würde, das Ergebnis umgekehrt wäre. Das soll natürlich nicht heißen, dass der Top-Down-Ansatz in irgendeiner Weise falsch wäre - sondern nur, dass hier kein korrektes Benchmarking gemacht wurde.
 

KonradN

Super-Moderator
Mitarbeiter
Der Code ist aber nicht falsch, nur weil es nicht so ist, wie Du ihn erwarten würdest. Und entweder Du bist überzeugt worden, dass es zu streng formuliert wurde (Erste Aussage) - dann sollte man diese aber nicht erneut genau so wiederholen - oder man formuliert besser.

So bleibt es bei meiner Aussage: Deine Aussage ist schlicht und einfach falsch. Der Code macht genau das, was Dein in #26 skizzierter Code auch macht. Und ja - das ist kein korrektes Benchmarking - genau so wie eben auch Dein skizziertes Vorgehen kein korrektes Benchmarking ist.
 

Arrive5784

Aktives Mitglied
Du musst schon genau lesen. Ich sprach in #26 nur von der Zeitmessung, nicht von einem vollumfänglichen Benchmarking. Aber #26 geht schon eher in die richtige Richtung eines akzeptablen Benchmarkings.
 

KonradN

Super-Moderator
Mitarbeiter
Kann es sein, dass Du den Code, den du kritisierst, nicht verstanden hast? Sonst hättest Du ja gesehen, dass der Code streng genommen nichts anderes macht als Du skizziert hast. Nur eben ist die zweite Zeitmessung in die Methode gezogen worden und der Aufruf der zu testenden Methode findet sich in dem Parameter.

Das mag Dir vom Aufbau her nicht gefallen (mir auch nicht), aber das macht es eben nicht "falsch".
 

Arrive5784

Aktives Mitglied
@MarvinsDepression Ich will nix sagen, aber bei deiner Methode funktioniert gar nichts... Das ist mir beim Test schreiben aufgefallen:

Java:
    public int[] findTopDown(int digits) {
        int[] r = {-1, -1};
        int a = (int) (Math.pow(10, digits) - 0.5);
        int b = a / 10 + 1;

        for (int i = a; i >= b; i--) {
            long product = buildPalindrome(i);

            for (int x = a; x >= b; x--) {
                int y = (int) (product / x);
                // if (y > x) break;
                if (y >= a && (long) x * y == product) {
                    r[0] = x;
                    r[1] = y;
                    return r;
                }
            }
        }
        return r;
    }

    private long buildPalindrome(int leftPart) {
        if (leftPart < 10) return leftPart;

        long palindrome = leftPart;

        while (leftPart > 0) {
            palindrome *= 10;
            palindrome += leftPart % 10;
            leftPart /= 10;
        }
        return palindrome;
    }

Hab nur das if (y > x) break; aus kommentiert.

Könntest du das bitte nochmal ändern, damit man ein Benchmarking machen kann... :)
 

Arrive5784

Aktives Mitglied
Ein Benchmark, so er funktionieren würde, würde dann in etwa so aussehen:

Java:
package PalindromeProduct;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.math.BigInteger;
import java.util.Arrays;

@State(Scope.Benchmark)
public class BenchmarkPalindromeProduct {
    @Param({"1", "2", "3", "4", "5"})
    public int arg;

    @Benchmark
    public void findBottomUp(Blackhole blackhole) {
        blackhole.consume(findBottomUp(arg));
    }

    @Benchmark
    public void findTopDown(Blackhole blackhole) {
        blackhole.consume(findTopDown(arg));
    }

    public void checkResults() {
        for (int i = 1; i < 5; i++) {
            System.out.println(i + " " + Arrays.equals(findBottomUp(i), findTopDown(i)));
        }
    }

    public int[] findBottomUp(int digits) {
        BigInteger fence = BigInteger.ZERO;
        int[] r = {-1, -1};
        int a = (int) Math.pow(10, digits) - 1;

        for (int i = a; i > 0; i--) {
            BigInteger b = BigInteger.valueOf(i);
            if (b.multiply(b).compareTo(fence) <= 0) {
                break;
            }
            for (int j = i; j > 0; j--) {
                BigInteger p = b.multiply(BigInteger.valueOf(j));
                if (p.compareTo(fence) > 0) {
                    if (palindrome(p)) {
                        fence = p;
                        r[0] = i;
                        r[1] = j;
                    }
                } else {
                    break;
                }
            }
        }
        return r;
    }

    public boolean palindrome(BigInteger p) {
        String s = String.valueOf(p);
        for (int i = 0; i < s.length() / 2; i++) {
            if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
                return false;
            }
        }
        return true;
    }

    public int[] findTopDown(int digits) {
        int[] r = {-1, -1};
        int a = (int) (Math.pow(10, digits) - 0.5);
        int b = a / 10 + 1;

        for (int i = a; i >= b; i--) {
            long product = buildPalindrome(i);

            for (int x = a; x >= b; x--) {
                int y = (int) (product / x);
                // if (y > x) break;
                if (y >= a && (long) x * y == product) {
                    r[0] = x;
                    r[1] = y;
                    return r;
                }
            }
        }
        return r;
    }

    private long buildPalindrome(int leftPart) {
        if (leftPart < 10) return leftPart;

        long palindrome = leftPart;

        while (leftPart > 0) {
            palindrome *= 10;
            palindrome += leftPart % 10;
            leftPart /= 10;
        }
        return palindrome;
    }

    public static void main(String[] args) throws Exception {
        new BenchmarkPalindromeProduct().checkResults();

        Options opt = new OptionsBuilder()
                .include(BenchmarkPalindromeProduct.class.getSimpleName())
                .forks(1)
                .warmupIterations(1)
                .measurementIterations(1)
                .measurementTime(TimeValue.seconds(10))
                .build();

        new Runner(opt).run();
    }
}

Wobei ich dann fairerweise vorher noch BigInteger entfernen muss.
 

M.L.

Top Contributor
Die Rätsel von Projekt Euler wollen (idR) mit möglichst wenig und vor allem geschickten Additions- und/oder Multiplikationsschritten bearbeitet werden. Und man kann Zusammenhänge nutzen um möglichst viele Rechenschritte am besten gar nicht erst ausführen zu müssen.

Das kleinste Ergebnis wäre 10.000 (100 * 100), das grösste 998.001 (999 * 999). Das Ergebnis hat -zwecks Palindrom Eigenschaft- die bereits genannte Struktur abccba (mit a>=b > c). Da 998.899 entfällt (grösser als erlaubt) käme 997.799 als (vrmtl. erste) zu prüfende Zahl für die passende Multiplikation infrage.
 

Arrive5784

Aktives Mitglied
@M.L. Das Problem ist an sich, dass @MarvinsDepression einen Algorithmus getestet hat, der überhaupt nicht funktioniert. Und das ist so Murks, dass ich auch nicht weiß, wie der zu verbessern wäre...

Zu Projekt Euler kann ich nicht viel sagen, bin ja kein Mathematiker. =)
 

Arrive5784

Aktives Mitglied
@httpdigest Liegt's an mir - oder funktioniert auch deine Variante nicht?

Java:
package PalindromeProduct;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.math.BigInteger;
import java.util.Arrays;

@State(Scope.Benchmark)
public class BenchmarkPalindromeProduct {
    @Param({"2", "3", "4", "5"})
    public int arg;

    @Benchmark
    public void findBottomUp(Blackhole blackhole) {
        blackhole.consume(arriveVariant(arg));
    }

    @Benchmark
    public void findTopDown(Blackhole blackhole) {
        blackhole.consume(httpdigestVariant(arg));
    }

    public void checkResults() {
        for (int i = 2; i <= 5; i++) {
            int[] a = arriveVariant(i);
            int[] b = httpdigestVariant(i);
            System.out.println(i + " " + Arrays.toString(a) + " " + Arrays.toString(b) + " " + Arrays.equals(a, b));
            System.out.println(b[0] * b[1]);
        }
    }

    public int[] arriveVariant(int digits) {
        BigInteger fence = BigInteger.ZERO;
        int[] r = {-1, -1};
        int a = (int) Math.pow(10, digits) - 1;

        for (int i = a; i > 0; i--) {
            BigInteger b = BigInteger.valueOf(i);
            if (b.multiply(b).compareTo(fence) <= 0) {
                break;
            }
            for (int j = i; j > 0; j--) {
                BigInteger p = b.multiply(BigInteger.valueOf(j));
                if (p.compareTo(fence) > 0) {
                    if (palindrome(p)) {
                        fence = p;
                        r[0] = i;
                        r[1] = j;
                    }
                } else {
                    break;
                }
            }
        }
        return r;
    }

    public boolean palindrome(BigInteger p) {
        String s = String.valueOf(p);
        for (int i = 0; i < s.length() / 2; i++) {
            if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
                return false;
            }
        }
        return true;
    }


    public int[] httpdigestVariant(int digits) {
        int upperBound = (int) Math.pow(10, digits) - 1;
        for (int v = upperBound; v > 0; v--) {
            int p = buildPalindrome(v, upperBound + 1);
            int d = findFirstDivisor(p, v, upperBound);
            if (d != -1) {
                return new int[]{d, v};
            }
        }
        return new int[]{-1, -1};
    }

    public int reverseInt(int v) {
        int r, res = 0;
        while (v > 0) {
            r = v % 10;
            res = res * 10 + r;
            v /= 10;
        }
        return res;
    }

    public int buildPalindrome(int num, int upperBound) {
        return num * upperBound + reverseInt(num);
    }

    public int findFirstDivisor(int palindrome, int v, int upperBound) {
        for (int i = upperBound; i >= (upperBound / 10 + 1) && i > v; i--)
            if (palindrome % i == 0)
                return i;
        return -1;
    }


    public static void main(String[] args) throws Exception {
        new BenchmarkPalindromeProduct().checkResults();
        System.exit(0);

        Options opt = new OptionsBuilder()
                .include(BenchmarkPalindromeProduct.class.getSimpleName())
                .forks(1)
                .warmupIterations(1)
                .measurementIterations(1)
                .measurementTime(TimeValue.seconds(10))
                .build();

        new Runner(opt).run();
    }
}

(Wollte diese nur generisch machen...)
 

httpdigest

Top Contributor
@httpdigest Liegt's an mir - oder funktioniert auch deine Variante nicht?
(Wollte diese nur generisch machen...)
Liegt an dir. Du lieferst in meinem von dir veränderten Code die zwei Werte:
Java:
return new int[]{d, v};
zurück und glaubst, dass das die beiden Faktoren sind. Sind sie aber nicht. `v` ist nicht ein Faktor des Palindroms, sondern die eine "Seite" des Palindroms, also bei 9009 wäre v = 90. Die Faktoren sind aber d = 99 und p/d = 91. p/d rechne ich aber niemals aus, weil es eben von der Aufgabenstellung überhaupt nicht gefordert war. Die Aufgabenstellung verlangte, dass man das Palindrom ermittelt. Nicht, dass man die Faktoren des Palindroms ermittelt/zurückgibt.
 
Zuletzt bearbeitet:

Arrive5784

Aktives Mitglied
interessant, danke für die Richtigstellung... hier nun der Vergleich:

Java:
package PalindromeProduct;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.Arrays;

@State(Scope.Benchmark)
public class BenchmarkPalindromeProduct {
    @Param({"2", "3", "4", "5", "6", "7", "8"})
    public int arg;

    @Benchmark
    public void findBottomUp(Blackhole blackhole) {
        blackhole.consume(arriveVariant(arg));
    }

    @Benchmark
    public void findTopDown(Blackhole blackhole) {
        blackhole.consume(httpdigestVariant(arg));
    }

    public void checkResults() {
        for (int i = 2; i <= 8; i++) {
            int[] a = arriveVariant(i);
            int[] b = httpdigestVariant(i);
            System.out.println(i + " " + Arrays.toString(a) + " " + Arrays.toString(b) + " " + Arrays.equals(a, b));
            System.out.println((long) b[0] * b[1]);
        }
    }

    public int[] arriveVariant(int digits) {
        long fence = 0;
        int[] r = {-1, -1};
        int a = (int) Math.pow(10, digits) - 1;

        for (int i = a; i > 0; i--) {
            if ((long) i * i <= fence) {
                break;
            }
            for (int j = i; j > 0; j--) {
                long p = (long) i * j;
                if (p > fence) {
                    if (palindrome(p)) {
                        fence = p;
                        r[0] = i;
                        r[1] = j;
                    }
                } else {
                    break;
                }
            }
        }

        return r;
    }

    public boolean palindrome(long p) {
        String s = String.valueOf(p);
        for (int i = 0; i < s.length() / 2; i++) {
            if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
                return false;
            }
        }
        return true;
    }

    public int[] httpdigestVariant(int digits) {
        int upperBound = (int) Math.pow(10, digits) - 1;
        for (int v = upperBound; v > 0; v--) {
            long p = buildPalindrome(v, upperBound + 1);
            int d = findFirstDivisor(p, v, upperBound);
            if (d != -1) {
                return new int[]{d, (int) (p / d)};
            }
        }
        return new int[]{-1, -1};
    }

    public int reverseInt(int v) {
        int r, res = 0;
        while (v > 0) {
            r = v % 10;
            res = res * 10 + r;
            v /= 10;
        }
        return res;
    }

    public long buildPalindrome(int num, int upperBound) {
        return (long) num * upperBound + reverseInt(num);
    }

    public int findFirstDivisor(long palindrome, int v, int upperBound) {
        for (int i = upperBound; i >= (upperBound / 10 + 1) && i > v; i--)
            if (palindrome % i == 0)
                return i;
        return -1;
    }

    public static void main(String[] args) throws Exception {
        new BenchmarkPalindromeProduct().checkResults();

        Options opt = new OptionsBuilder()
                .include(BenchmarkPalindromeProduct.class.getSimpleName())
                .forks(1)
                .warmupIterations(1)
                .measurementIterations(1)
                .measurementTime(TimeValue.seconds(10))
                .build();

        new Runner(opt).run();
    }
}

Code:
Benchmark                                                  (arg)   Mode  Cnt        Score   Error  Units
PalindromeProduct.BenchmarkPalindromeProduct.findBottomUp      2  thrpt       1887737,694          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findBottomUp      3  thrpt          6442,840          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findBottomUp      4  thrpt         13794,175          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findBottomUp      5  thrpt            28,274          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findBottomUp      6  thrpt            90,531          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findBottomUp      7  thrpt             0,097          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findBottomUp      8  thrpt             0,711          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findTopDown       2  thrpt       3128905,902          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findTopDown       3  thrpt         37814,461          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findTopDown       4  thrpt         30451,355          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findTopDown       5  thrpt          2376,012          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findTopDown       6  thrpt           244,949          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findTopDown       7  thrpt            12,071          ops/s
PalindromeProduct.BenchmarkPalindromeProduct.findTopDown       8  thrpt             1,924          ops/s

Je nach Input variieren die Zeiten natürlich stark. Aber für digits=7 ist deine Variante ca. 120mal schneller als mein Ansatz. :D Dem ist wohl nichts hinzuzufügen.
 

DrPils

Bekanntes Mitglied
Naja, weil es sehr schlecht programmiert war, nicht funktionierte und du das obendrein offensichtlich nicht erkannt hasa
Wieso war sein Code schlecht programmiert?
War aufjedenfall besser als deine Lösung die du ja von mir kopiert hast, äh sorry zeitversetzt gepostet hast. Funktionieren tut er ja auch.
Sein Code läuft bei ihm, ich kopiere sein Code, der Code läuft. Du kopierst ihn, änderst paar Sachen, er funktioniert nichtmehr....
Wo liegt der Fehler?
 

Arrive5784

Aktives Mitglied
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.

Find the largest palindrome made from the product of two 3-digit numbers.
Ich finde, man kann das so oder so lesen, also, dass sowohl die numbers/factors gesucht sind als auch das palindrome. Ohne die Faktoren kann ich doch gar nicht verifizieren, ob das ein gültiges Palindrom ist...

@httpdigest Wo kommen die Zeitunterschiede her? Wenn ich das richtig sehe, ist deins wie meins in O(n²). Dein Ansatz ist aber durchschnittlich doppelt so schnell.

argModeScore AScore BA/B
6avgt0,0110,0042,75
7avgt10,7680,089120,9
8avgt1,4240,5272,7
 

MarvinsDepression

Bekanntes Mitglied
Wenn ich mich nochmal dazu schalten darf (als Murkser, der Code schreibt, welcher opensightly nur auf dem eigenen Rechner funktioniert :D):
Von Benchmarks hab ich tatsächlich keine Ahnung. Was sagt so ein Score aus. Sekunden/Durchlauf? Oder ganz was anderes?
 

KonradN

Super-Moderator
Mitarbeiter
Also wenn Du Dich für Benchmarks interessierst, dann kannst Du ein paar Links dazu betrachten:

Aber das ist ein Thema, ohne das man auch sehr gut klar kommt (um es mal so einfach auszudrücken).

Zumindest ich würde andere Themen als deutlich wichtiger ansehen, wie z.B. Clean Code, OO Design und so. Das ist um ein vielfaches wichtiger (in meinen Augen). Und bezüglich diesen Benchmarks würde ich einfach das Thema Optimizations zitieren, zu dem schlicht gesagt wird: Don't do it. (Und für Experten, die wissen, was sie tun: Don't do it, yet)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
sserio Größtes Palindrom-Produkt Programm funktioniert nur halb Java Basics - Anfänger-Themen 23
L Rekursion größtes Zeichen Java Basics - Anfänger-Themen 8
K Comparable - Objekte aus Array vergleichen und größtes auswählen Java Basics - Anfänger-Themen 1
W Größtes Element im unsortierten Binärbaum Java Basics - Anfänger-Themen 7
F Größtes Produkt in einem String Java Basics - Anfänger-Themen 4
A Palindrom Java Basics - Anfänger-Themen 3
H Palindrom ermitteln Java Basics - Anfänger-Themen 21
N palindrom erkennen Java Basics - Anfänger-Themen 3
H Harshad-Zahl (Nivenzahl) und Palindrom überprüfen Java Basics - Anfänger-Themen 2
L Palindrom in zweidimensionalem Array Java Basics - Anfänger-Themen 16
B Palindrom Test mit Junit Java Basics - Anfänger-Themen 23
T Auf Palindrom überprüfen Java Basics - Anfänger-Themen 10
R Best Practice Palindrom in einem Text finden Java Basics - Anfänger-Themen 18
L In Javakara Palindrom erkennen. Java Basics - Anfänger-Themen 9
P Programm Hilfe Palindrom Java Basics - Anfänger-Themen 6
C Bei der LinkedList auf Palindrom überprüfen Java Basics - Anfänger-Themen 4
Y Rekursiv Palindrom herausfinden Java Basics - Anfänger-Themen 5
C Palindrom im array Java Basics - Anfänger-Themen 5
R Palindrom eines int-Arrays berechnen Java Basics - Anfänger-Themen 14
D Palindrom Java Basics - Anfänger-Themen 15
H Palindrom Programm Java Basics - Anfänger-Themen 8
K Palindrom Test Java Basics - Anfänger-Themen 9
C Überprüfen auf Palindrom Java Basics - Anfänger-Themen 12
P Palindrom Java Basics - Anfänger-Themen 10
R In einem Palindrom 2 Buchstaben vergleichen Java Basics - Anfänger-Themen 16
H Palindrom Java Basics - Anfänger-Themen 7
NoXiD Auf Palindrom Prüfen Java Basics - Anfänger-Themen 9
M Palindrom mit Groß & kleinbuchstaben Java Basics - Anfänger-Themen 19
M Palindrom Test mit Char-arrays! Java Basics - Anfänger-Themen 3
B Produkt eines double - streams Java Basics - Anfänger-Themen 3
Dorfschmied Kartesisches Produkt von zwei Liste mit Hashmaps<String,String> erstellen Java Basics - Anfänger-Themen 4
F Produkt d. Ziffern einer Zahl..?! Java Basics - Anfänger-Themen 5
T Produkt eines mehrdimensionalen Arrays Java Basics - Anfänger-Themen 5
Ocram Methoden Produkt eines Intervalls Java Basics - Anfänger-Themen 11
H pi näherungsweise berechnen - Wallis Produkt Java Basics - Anfänger-Themen 9
R Produkt berechnen Java Basics - Anfänger-Themen 23
E Algorithmus für kart. Produkt: als int [] Feld repräsentiert Java Basics - Anfänger-Themen 10
T Produkt 1-10 mit Zwischenschritten Java Basics - Anfänger-Themen 7
N Matrix Matrix Produkt Java Basics - Anfänger-Themen 7
D The constructor Bestellung(Bestellung.Produkt, Bestellung.Kunde) is undefined Java Basics - Anfänger-Themen 15
B Produkt ohne Multiplizieren? Java Basics - Anfänger-Themen 7
N Produkt Java Basics - Anfänger-Themen 2
U Summe produkt von einem array Java Basics - Anfänger-Themen 9
T Geht so was? public void verkaufe (<X implements Produkt& Java Basics - Anfänger-Themen 8
S mehrere Erweiterungen fürs Produkt Java Basics - Anfänger-Themen 6
J tast-Eingabe_(Vektor)Skalar-produkt Java Basics - Anfänger-Themen 19

Ähnliche Java Themen

Neue Themen


Oben