10

BigIntegerを使用せずにカラツバアルゴリズムをJavaで実装しようとしています。私のコードは、両方の整数が同じで桁数が同じ場合にのみ適用されます。正しい答えは得られませんが、正しい答えにかなり近い答えが得られます。たとえば、12*12 の場合は 149 になります。私は自分のコードの何が問題なのかを理解できません。なぜなら、私は(本によって)すべてを正しく行ったと信じているからです。これが私のコードです。

public static void main(String[] args) {
    long ans=karatsuba(12,12);
    System.out.println(ans);
}

private static long karatsuba(long i, long j) {
    if (i<10 || j<10){
        return i*j;
    }
    int n=getCount(i);
    long a=(long) (i/Math.pow(10, n/2));
    long b=(long) (i%Math.pow(10, n/2));
    long c=(long) (j/Math.pow(10, n/2));
    long d=(long) (j%Math.pow(10, n/2));

    long first=karatsuba(a,c);
    long second=karatsuba(b,d);
    long third=karatsuba(a+b,c+d);

    return ((long) ((first*Math.pow(10, n))+((third-first-second)*Math.pow(10,n/2))+third));
}

private static int getCount(long i) {
    String totalN=Long.toString(i);
    return totalN.length();
}

編集:

Ziyao Wei のおかげで、問題は「3 番目」を「2 番目」に置き換えることでした。ただし、現在別の問題があります。

からつば(1234,5678)を呼ぶと正解が出ますが、からつば(5678,1234)を呼ぶと正解が出ません。誰かがその理由を知っている可能性がありますか?私の更新されたコードは次のとおりです。

public static void main(String[] args) {
    //wrong answer
    long ans=karatsuba(5678,1234);
    System.out.println(ans);

    //correct answer
    long ans1=karatsuba(1234,5678);
    System.out.println(ans1);
}

private static long karatsuba(long i, long j) {
    if (i<10 || j<10){
        return i*j;
    }

    int n=getCount(i);

    long a=(long) (i/Math.pow(10, n/2));
    long b=(long) (i%Math.pow(10, n/2));
    long c=(long) (j/Math.pow(10, n/2));
    long d=(long) (j%Math.pow(10, n/2));

    long first=karatsuba(a,c);
    long second=karatsuba(b,d);
    long third=karatsuba(a+b,c+d);

    return ((long) ((first*Math.pow(10, n))+((third-first-second)*Math.pow(10, n/2))+second));

}

アップデート:

「n/2」の値を切り上げることができたので、問題は解決しましたが、4 桁を超える数字を使用するとバグが発生します。これが私の更新されたコードです:

public static void main(String[] args) {
    System.out.println(Math.round(5.00/2));

    //correct answer
    long ans=karatsuba(5678,1234);
    System.out.println(ans);

    //correct answer
    long ans1=karatsuba(1234,5678);
    System.out.println(ans1);

    //wrong answer
    long ans2=karatsuba(102456,102465);
    System.out.println(ans2);
}


private static long karatsuba(long i, long j) {
    if (i<10 || j<10){
        return i*j;
    }

    double n=Math.round(getCount(i));

    long a=(long) (i/Math.pow(10, Math.round(n/2)));
    long b=(long) (i%Math.pow(10, Math.round(n/2)));
    long c=(long) (j/Math.pow(10, Math.round(n/2)));
    long d=(long) (j%Math.pow(10, Math.round(n/2)));

    long first=karatsuba(a,c);
    long second=karatsuba(b,d);

    long third=karatsuba(a+b,c+d);

    return ((long) ((first*Math.pow(10, Math.round(n)))+((third-second-first)*Math.pow(10, Math.round(n/2)))+second));

}

private static double getCount(long i) {
    String totalN=Long.toString(i);

    return totalN.length();
}

BigInteger を使用せずに、より大きな数 (4 桁以上) の解決策を誰かが思いついた場合は、お知らせください。ありがとう。

4

9 に答える 9

5

あなたの式は間違っています。

最初 * Math.pow(10, n) + (3 番目 - 最初 - 2 番目) * Math.pow(10, n / 2) + 3 番目

間違っています。式は

最初 * Math.pow(10, n) + (3 番目 - 最初 - 2 番目) * Math.pow(10, n / 2) + 2 番目

ウィキペディア:

z0 = karatsuba(low1,low2)
z1 = karatsuba((low1+high1),(low2+high2))
z2 = karatsuba(high1,high2)
return (z2*10^(m))+((z1-z2-z0)*10^(m/2))+(z0)
于 2013-07-08T16:06:39.423 に答える
1

最後に、数時間考えた後、正しい解決策を見つけました。

public static long karatsuba(long i, long j) {
    if (i < 10 || j < 10) {
        return i * j;
    }
    double n = Math.round(getCount(i));
    if (n % 2 == 1) {
        n++;
    }
    long a = (long) (i / Math.pow(10, Math.round(n / 2)));
    long b = (long) (i % Math.pow(10, Math.round(n / 2)));
    long c = (long) (j / Math.pow(10, Math.round(n / 2)));
    long d = (long) (j % Math.pow(10, Math.round(n / 2)));

    long first = karatsuba(a, c);
    long second = karatsuba(b, d);
    long third = karatsuba(a + b, c + d);

    return ((long) ((first * Math.pow(10, n)) + ((third - first - second) * Math.pow(10, Math.round(n / 2))) + second));
}

n が odd にならない理由を説明することはできませんが、今のところ、私が書いた多数のテストで乗算が正しく機能しています。この動作については、わかり次第説明します。

更新:私はコースラのアルゴリズム: 設計と分析、パート 1 コースを受講しており、この動作に関する質問を投稿しました。アンドリュー・パットンによる回答は次のとおりです。

他の場所で述べたように、入力を分割する際の鍵は、a と c が係数として同じ 10 の累乗を持つように、b と d が同じ長さであることを確認することです。その力が何であれ、あなたの n/2 になります。... したがって、10^n の n の値は、実際には入力の全長ではなく、n/2*2 です。

したがって、例に続く3桁の数字の場合:

n = 3;   
n/2 = 2;
n != n/2 * 2;

したがって、この例では、n は n/2 * 2 = 4 と等しくなければなりません。

これが理にかなっていることを願っています。

于 2015-01-19T19:24:26.363 に答える
0

i=a*B+b と j=c*B+d を設定します。ここで、B=10^m と m=n/2 です。それで

i*j=B^2*(a*c)+B*(a*c+b*d-(a-b)*(c-d))+c*d

ただし、半分のケースでは、B^2=10^(2m) は 10^n と等しくありません。奇数 n の場合、n=2*m+1 になるため、この場合、B^2=10^(n -1)。

したがって、一度 m=n/2 or better を定義し、それを使用して a,b,c,d を計算し、最終的な合計で係数 B*B を使用することをお勧めしm=(n+1)/2ますB=(long)Math.pow(10,m)

于 2014-03-04T22:03:35.697 に答える
0

正しいアプローチは次のとおりです(回答が変更されました):

public class KaratsubaMultiplication {

public static void main(String[] args) {
    //correct answer
    long ans=karatsuba(234,6788);
    System.out.println("actual    " + ans);
    System.out.println("expected  " + 234*6788);

    long ans0=karatsuba(68,156);
    System.out.println("actual    " +ans0);
    System.out.println("expected  " + 68*156);

    long ans1=karatsuba(1234,5678);
    System.out.println("actual    " + ans1);
    System.out.println("expected  " + 1234*5678);

    long ans2=karatsuba(122,456);
    System.out.println("actual    " +ans2);
    System.out.println("expected  " + 122*456);

    long ans3=karatsuba(102456,102465);
    System.out.println("actual    " + ans3);
    System.out.println("expected  " + 102456l * 102465l);
}


private static long karatsuba(long i, long j) {
    if (i<10 || j<10){
        return i*j;
    }

    double n=Long.toString(i).length();


    long a=(long) (i/Math.pow(10, Math.floor(n/2d)));
    long b=(long) (i%Math.pow(10, Math.floor(n/2d)));
    long c=(long) (j/Math.pow(10, Math.floor(n/2d)));
    long d=(long) (j%Math.pow(10, Math.floor(n/2d)));

    long first=karatsuba(a,c);

    long second=karatsuba(b,d);

    long third=karatsuba(a+b,c+d);

    return (long) (
           (first * Math.pow(10, Math.floor(n/2d) * 2)) +
           ((third-second-first) * Math.pow(10, Math.floor(n/2))) +
           second
           );

}

}
于 2014-03-04T20:35:19.743 に答える
0

入力サイズが奇数の場合、Math.round() で四捨五入する代わりに、入力サイズ (x または y の桁数の最小値) の値に 1 を追加しています。たとえば、X = 127 & Y = 162 の場合、入力サイズは 3 です。1 ずつ増やして 4 にします。次に、a = X/Math.pow(10,input_size/2) = 1. b = X%Math .pow(10,input_size/2) = 27. 同様に、c = 1 および d = 62. ここで、X*Y = (ac)*Math.pow(10,input_size)+(ad+bc)* を計算するとMath.pow(10,input_size/2)+bd; それは正しい答えを与えます。入力サイズが奇数の場合にのみ 1 ずつインクリメントすることを覚えておいてください。完全な実装はこちら - https://github.com/parag-vijay/data_structures/blob/master/java/KaratsubaMultiplication.java

于 2017-08-15T03:58:33.727 に答える
0
first * Math.pow(10, 2*degree) + (third - first - second) * Math.pow(10, degree) + second

どこ

degree = floor(n/2)

丸めはありません、少なくともそれは私が知っていることです...

例えば、

normal: 5/2 = 2.5
floor: 5/2 = 2
round: 5/2 = 3

したがって、あなたがすることは...

round(n)

と同じではありません

2*floor(n/2)

それが私が思うことです、

よろしく

于 2013-08-28T15:20:09.367 に答える