【问题标题】:Cutting of the first 9 digits切割前 9 位数字
【发布时间】:2013-03-07 04:19:08
【问题描述】:

好吧,我正在尝试解决我的一个朋友让我去做的挑战,我已经设法从 BigInteger 中删除最后 9 位数字,我有办法删除前 9 位,但是太慢了,时间太长了。

我需要前 9 个和后 9 个的原因是因为我正在寻找一个 BigInteger,其中第一个和最后一个是泛数字的。

如果您不明白我的意思是说我们有n = new BigInteger("123456789987654321"),那么我需要分别获取“123456789”和“987654321”,并且我确实想要将 BigInteger 转换为一个字符串,因为这是一个非常缓慢的过程。

我在这里追求速度,我只是对这个解决方案感到困惑。我听说过一些关于使用黄金比例的事情?如果您有兴趣,这是我的代码。

import java.math.BigInteger;

public class Main {

    public static void main(String...strings)
    {       
        long timeStart = System.currentTimeMillis();
        fib(350_000);
        long timeEnd = System.currentTimeMillis();
        System.out.println("Finished processing, time: " + (timeEnd - timeStart) + " milliseconds.");
    }

    public static BigInteger fib(int n)
    {
        BigInteger prev1 = BigInteger.valueOf(0), prev2 = BigInteger.valueOf(1);        
        for (int i = 0; i < n; i++)
        {   

            // TODO: Check if the head is pandigital as well.
            BigInteger tailing9Digits = tailing9Digits(prev1);
            boolean tailPandigital = isPanDigital(tailing9Digits);

            if (tailPandigital)
            {                   
                System.out.println("Solved at index: " + i);
                break;
            }
            BigInteger savePrev1 = prev1;
            prev1 = prev2;
            prev2 = savePrev1.add(prev2);
        }
        return prev1;
    }

    public static BigInteger leading9Digits(BigInteger x)
    {
        // STUCK HERE.
        return null;
    }

    public static BigInteger tailing9Digits(BigInteger x) 
    {
        return x.remainder(BigInteger.TEN.pow(9));
    }

    static BigInteger[] pows = new BigInteger[16];

    static 
    {
        for (int i = 0; i < 16; i++) 
        {
            pows[i] = BigInteger.TEN.pow(i);
        }
    }

    static boolean isPanDigital(BigInteger n) 
    {
        if (!n.remainder(BigInteger.valueOf(9)).equals(BigInteger.ZERO)) 
        {
            return false;
        }
        boolean[] foundDigits = new boolean[9];


        boolean isPanDigital = true;
        for (int i = 1; i <= 9; i++) 
        {
            BigInteger digit = n.remainder(pows[i]).divide(pows[i - 1]);
            for (int j = 0; j < foundDigits.length; j++) {
                if (digit.equals(BigInteger.valueOf(j + 1)) && !foundDigits[j])
                {
                    foundDigits[j] = true;
                }
            }
        }

        for (int i = 0; i < 9; i++)
        {
            isPanDigital = isPanDigital && foundDigits[i];
        }

        return isPanDigital;
    }
}

【问题讨论】:

  • 你取得的最快速度是多少?什么输入?
  • 嗨,Thomas,如果我理解正确,您想要一个高效/快速的算法来获取 BigInteger 的前 9 位数字?我建议您明确提及您需要帮助的地方。另外,可能包括算法等标签。
  • 重做你昨天的问题,为什么? stackoverflow.com/questions/15216777/…
  • @SeanF 上次的问题是关于拖尾 9 位的问题。
  • 为了确定一个数字在给定的基础上是否是泛数字的,您必须在该基础上表达它并检查结果字符串。您必须将其转换为字符串。

标签: java algorithm math fibonacci


【解决方案1】:

BigInteger 如果您关心速度,我不建议您使用。它的大多数方法都实现得很差,这通常会导致代码非常慢。

除法和基数转换有一个分而治之的技巧,您可能会发现它很有帮助。

首先,BigIntegermultiply() 是二次的。你需要解决这个问题,否则这些分而治之的技巧不会导致任何加速。通过快速傅里叶变换进行乘法运算相当快且效果好。

如果您想将 BigInteger 转换为 base-10,请将其分成两半(按位)并将其写为 a * 256^k + b. 您可以做的一件事是将 ab 递归转换为 base-10,然后通过重复平方将256^k 转换为十进制,然后以10 为底,将a 乘以256^k 并将b 添加到结果中。此外,由于您只对前几位数字感兴趣,因此如果 a * 256^k 的前几位数字不会受到像 b 这样小的东西的影响,您甚至可能不需要转换 b

类似的技巧适用于除法。

您可以使用toByteArray() 方法进行位移和提取。

【讨论】:

  • 你能解释一下我将如何通过移位获得BigInteger的前9位数字吗?
  • 也许我不清楚。您可以使用toByteArray() 将数字分解为递归中的“上半部分”和“下半部分”。
  • 我试过这个:pastie.org/6409396,输出看起来像这样:pastie.org/6409398。你能解释一下这有什么帮助吗?
【解决方案2】:

我相信这就是你需要的:

import java.math.BigInteger;

public class PandigitalCheck {

    public static void main(String[] args) {

        BigInteger num = new BigInteger("12345678907438297438924239987654321");
        long timeStart = System.currentTimeMillis();
        System.out.println("Is Pandigital: " + isPandigital(num));
        long timeEnd = System.currentTimeMillis();
        System.out.println("Time Taken: " + (timeEnd - timeStart) + " ms");
    }

    private static boolean isPandigital(BigInteger num) {
        if (getTrailing9Digits(num).compareTo(getLeading9Digits(num)) == 0) {
            return true;
        }

        return false;
    }

    private static BigInteger getLeading9Digits(BigInteger num) {
        int length = getBigIntLength(num);
        BigInteger leading9 = BigInteger.ZERO;
        for (int i = 0; i < 9; i++) {
            BigInteger remainder = num.divide(BigInteger.TEN.pow(length - 1 - i));
            leading9 = leading9.add(remainder.multiply(BigInteger.TEN.pow(i)));
            num = num.remainder(BigInteger.TEN.pow(length - 1 - i));
        }

        return leading9;
    }

    private static int getBigIntLength(BigInteger num) {
        for (int i = 1; ; i++) {
            if (num.divide(BigInteger.TEN.pow(i)) == BigInteger.ZERO) {
                return i;
            }
        }
    }

    private static BigInteger getTrailing9Digits(BigInteger num) {
        return num.remainder(BigInteger.TEN.pow(9));
    }

}

输出是:

Is Pandigital: true
Time Taken: 0 ms

它符合要求吗?

【讨论】:

  • 由于我正在运行如此大的循环,我得到一个负指数的异常:BigInteger remainder = num.divide(BigInteger.TEN.pow(length - 1 - i));
  • 我知道这与“- 1 - i”有关,有没有办法解决这个问题?
  • 您收到此错误的输入是什么?如果它太大而无法在此处发布,您可以使用 pastebin.com。
  • 这是因为斐波那契数列的前2个数是0。
  • 我没听懂你。斐波那契数列是如何发挥作用的?如果您可以提供您提供的输入,那么也许我可以调试我的代码。
【解决方案3】:

我是 java 新手,我只是将 BigInteger 转换为 String,但它比你的代码快一点

import java.math.BigInteger;
public class Main {
    public static void main(String args[])
    {       
        long timeStart = System.currentTimeMillis();
        String biStr = new BigInteger("123456789987654321").toString();
        int length=(biStr.length())/2;
        String[] ints = new String[length];
        String[] ints2 = new String[length];
        for(int i=0; i<length; i++) {
            int j=i+length;
            ints[i] = String.valueOf(biStr.charAt(i));
            ints2[i] = String.valueOf(biStr.charAt(j));
            System.out.println(ints[i] +"  |  "+ints2[i]);
        }
        long timeEnd = System.currentTimeMillis();
        System.out.println("Finished processing, time: " + (timeEnd - timeStart) + " milliseconds.");
    }
}

【讨论】:

    【解决方案4】:

    也许这不是太快,但至少它很简单

        BigInteger n = new BigInteger("123456789987654321");
        BigInteger n2 = n.divide(BigInteger.TEN.pow(new BigDecimal(n).precision() - 9));
        BigInteger n1 = n.remainder(new BigInteger("1000000000"));
        System.out.println(n1);
        System.out.println(n2);
    

    输出

    987654321
    123456789
    

    【讨论】:

    • 但他说这个数字可以是任何数字。它不一定是 18 位长。
    • 对于1234567899987654321 的输入,我得到9987654321123456789 作为输出。这是不正确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多