如何在没有支票的情况下生成回文列表

时间:2017-05-25 10:10:58

标签: java optimization

我正在解决一个问题,我需要操作大量的回文单列表到一定数量的数字。这应该适用于15位数的数字。我见过的最常见的方法是迭代每个数字并检查每个是否是回文然后将其添加到列表中。这是我在java中的实现,它工作正常。

public class Palindrome {

public ArrayList<Long> List = new ArrayList<Long>();
public double d;

public Palindrome(double digits) {
    this.d = digits;

    long dig = (int)(Math.pow(10,digits));

    for (long i = 1; i <= dig; i++) {

        long a = i;
        long b = inverse(a);
        if (a == b) {
            List.add(a);
        }
    }

public long inverse(long x){
    long inv = 0;
    while (x > 0) {
        inv = inv * 10 + x % 10;
        x = x / 10;
    }
    return inv;
}

}

唯一的问题是,当我达到10位以上的回文时它会非常慢。我一直在考虑创建这个列表的其他方法,我考虑的一个考虑因素是生成回文列表,而不是遍历每个数字并检查它是否是回文。

我还在纸上工作,但模式不像我想的那样明显我会发现变成伪代码。我正在研究n个数字,从i到n,如果数字是偶数,则生成从1到[10 ^(i / 2 + 1) - 1]的数字。然后将每个数字的反向追加到自身。有点坚持如何为奇数位做。那就是我现在的位置。

如果我弄清楚并执行代码,我会回复我自己的回复,但在此期间,我想知道是否有人之前已经这样做过或者有一种我忽略的替代方法会更多高效。

更新

所以我确实通过你的所有建议设法解决了问题。我决定使用数字作为字符串,但与我的意图相反,这实际上增加了运行时:/

public class Palindrome2 {
public ArrayList<Long> List = new ArrayList<Long>();
public double d;

public Palindrome2(double digits) {
    this.d = digits;

    for (long n = 1; n <= d; n++) {
        if (n == 1) {
            for (long i = 1; i < 10; i++) {
                List.add(i);
            }
        }

        if (n % 2 != 0 && n != 1) {
            long max = (long) Math.pow(10, (n + 1) / 2);
            long min = (long) Math.pow(10, Math.floor(n / 2));

            for (long i = min; i < max; i++) {
                String str = Long.toString(i);
                str = str + removeFirst(reverse(str));
                Long x = Long.parseLong(str);
                List.add(x);
            }
        } else if (n % 2 == 0) {
            long max = (long) (Math.pow(10, Math.floor((n + 1) / 2)) - 1);
            long min = (long) Math.pow(10, (n / 2) - 1);

            for (long i = min; i <= max; i++) {
                String str = Long.toString(i);
                str = str + reverse(str);
                Long x = Long.parseLong(str);
                List.add(x);
            }
        }
    }
}

public String reverse(String x) {
    String rev = new StringBuffer(x).reverse().toString();
    return rev;
}

public String removeFirst(String x) {
    return x.substring(1);
}

}

再一次,准确但仍然缓慢:(

5 个答案:

答案 0 :(得分:1)

简介

您需要在跳转到开发之前大致分析算法的常规模式,这样可以节省大量时间,例如:

each 1 digit is 1 palindrome, e.g: 1
each 2 digits has 1 palindrome, e.g: 11.

each 3 digits has 10 palindromes, e.g: 101,111,...,191.
each 4 digits has 10 palindromes, e.g: 1001, 1111, ..., 1991.

each 5 digits has 100 palindromes, e.g: 10001, 11011, ..., 19091, ..., 19991.
each 6 digits has 100 palindromes, e.g: 100001, 110011, ..., 190091, ..., 199991.

each 7 digits has 1000 palindromes, e.g: 1000001, ...,1900091,...,1090901, ..., 1999991.
each 8 digits has 1000 palindromes, e.g: 10000001, ...,19000091,...,10900901, ..., 19999991.

....

然后你可以编写一些安排算法来实现它。

实施

但是我可以告诉你这个实现可以进一步优化,如果你使用缓存来保存从低位palindromes(2)生成的回文,那么任何高位数palindromes(n>2)都可以重用它。

也许它不是健壮,但它通过我github的所有测试。我把剩下的工作留给了他们优化给你,我希望你能独自完成。

private static List<Integer> palindromes(int digits) {
    return palindromes(digits, 0);
}

private static List<Integer> palindromes(int digits, int shifts) {
    List<Integer> result = new ArrayList<>();
    int radix = (int) Math.pow(10, digits - 1);
    int renaming = digits - 2;
    boolean hasRenaming = renaming > 0;
    for (int i = start(digits, shifts); i <= 9; i++) {
        int high = i * radix;
        int low = low(digits, i);
        if (hasRenaming) {
            for (Integer m : palindromes(renaming, shifts + 1)) {
                int ret = high + m * 10 + low;
                if (ret < 0) {
                    return result;
                }
                result.add(ret);
            }
        } else {
            result.add(high + low);
        }
    }
    return result;
}

private static int low(int digits, int high) {
    return digits > 1 ? high : 0;
}

private static int start(int digits, int shifts) {
    return digits > 1 && shifts == 0 ? 1 : 0;
}

用法

然后你可以收集所有回文数字如下:

           //  v--- min:0, max: 2147447412, count: 121474
List<Integer> all = IntStream.rangeClosed(1, 10)
            .mapToObj(PalindromeTest::palindromes)
            .flatMap(List::stream)
            .collect(Collectors.toList());

时间成本:

  

191ms

启用缓存

public class Palindromes {
    private static final int[] startingNonZerosTable = {
            0,// 0
            0, 1,// 1 2
            10, 10,//3 4
            100, 100, //5 6
            1000, 1000,//7 8
            10000, 10000,// 9 10
            100000, 100000,//11 12
            1000000, 1000000,//13 14
            10000000, 10000000,//15 16
            100000000, 100000000,//17 18
            1000000000, 1000000000//19 20
    };

    private static final int MAX_DIGIT = 9;
    private static final int MIN_DIGIT = 0;
    private static final int RADIX = MAX_DIGIT - MIN_DIGIT + 1;
    private static final int LONG_MAX_DIGITS = 19;
    private static volatile long[][] cache = new long[LONG_MAX_DIGITS + 1][];
    //                                      includes palindromes(0)  ---^

    static {
        cache[0] = new long[0];
        cache[1] = new long[]{0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L};
        cache[2] = new long[]{0L, 11L, 22L, 33L, 44L, 55L, 66L, 77L, 88L, 99L};
    }

    public static LongStream since1(int end) {
        return between(1, end);
    }

    public static LongStream between(int start, int end) {
        return IntStream.rangeClosed(start, end)
                        .mapToObj(Palindromes::of)
                        .flatMapToLong(identity());
    }

    public static LongStream of(int digits) {
        return Arrays.stream(palindromes0(digits))
                     .skip(startingNonZerosTable[digits]);
    }

    private final static long[] palindromes0(int digits) {
        if (cache[digits] != null) {
            return cache[digits];
        }

        long[] result = new long[sizeOf(digits)];
        int size = 0;
        long high = (long) Math.pow(RADIX, digits - 1);

        for (int i = MIN_DIGIT; i <= MAX_DIGIT; i++) {
            for (long mid : palindromes0(digits - 2)) {
                long value = i * high + mid * RADIX + i;
                if (value < 0) {//overflow
                    return cache[digits] = Arrays.copyOf(result, size);
                }
                result[size++] = value;
            }
        }
        return cache[digits] = result;
    }

    private static int sizeOf(int digits) {
        return MAX_DIGIT * (int) Math.pow(RADIX, (digits - 1) >>> 1)
                + startingNonZerosTable[digits];
    }

    //                  v--- java -Xms1024m -Xmx2048m test.algorithm.Palindromes
    public static void main(String[] args) {
        Duration duration = timing(() -> {
                         // palindromes[1..15] ---v
            LongSummaryStatistics result = since1(15).summaryStatistics();
            long max = result.getMax();
            long count = result.getCount();

            System.out.printf("Max: %d, Count: %d%n", max, count);
        });

        System.out.printf("Time Elapsed:%s%n", duration);
                                      // ^--- time elapsed: 4s 
    }

    private static Duration timing(Runnable task) {
        long starts = System.currentTimeMillis();
        task.run();
        return Duration.ofMillis(System.currentTimeMillis() - starts);
    }
}

时间成本:

  

回文[1 ... 15]时间过去了:4s

答案 1 :(得分:1)

您是否尝试过使用字符而不是数字?您可以将回文生成为一串数字,然后在最后转换为数字。像这样的伪代码:

generatePalindrome(size)

    half <- size DIV 2 // Integer division

    result <- ""

    result.append(randomDigitIn(1..9))  // No leading zeros.

    while (result.length <= half)
      result.append(randomDigitIn(0..9))
    endwhile

    if (size is odd)
      result <- result + randomDigitIn(0..9) + result.reverse()
    else
      result <- result + result.reverse()
    endif

    return number.parse(result)

end generatePalindrome()

基本上你随机产生一半的回文,避免前导零,在中间插入一个额外的数字用于奇数长度,追加反转的前半部分,然后将数字字符串解析成你想要的数字格式。

答案 2 :(得分:0)

对于奇数位,您可以简单地重复使用前一步骤中生成的回文,将它们分成两半,并在中间插入从0到9的所有可能数字。

假设您需要生成3位数的回文,只需获取2位数的所有回文并添加从0到9的所有数字。

我们有22比我们可以生成:

  • 202
  • 212
  • 222
  • 232

希望我的想法很清楚:)

答案 3 :(得分:0)

尝试这样的事情:

public class Palindrome
{
  public static ArrayList<Long> calculatePalindromes(int maxLength) {
    ArrayList<Long> result = new ArrayList<>();
    if (maxLength <= 0) {
      return result;
    }
    long maxPart = (long)Math.pow(10, maxLength / 2);
    for (long i = 0; i < 10; ++i) {
      result.add(i);
    }
    for (long i = 1; i < maxPart; ++i) {
      long curHalf = i;
      long curNum = i;
      int curLen = 0;
      while (curHalf != 0) {
        curNum *= 10;
        curNum += curHalf % 10;
        curHalf /= 10;
        ++curLen;
      }
      result.add(curNum);

      // insert numbers from 0 to 9
      if (curLen * 2 + 1 > maxLength) {
        continue;
      }
      for (int j = 0; j < 10; ++j) {
        curHalf = i;
        curNum = i;
        curNum *= 10;
        curNum += j;
        while (curHalf != 0) {
          curNum *= 10;
          curNum += curHalf % 10;
          curHalf /= 10;
        }
        result.add(curNum);
      }
    }
    return result;
  }
}

我们的想法是在每个0之后插入9X的数字,然后在reversed(X)之后添加X (1..9) reversed(X),这样我们就会得到 File f = new File(path); FileOutputStream fileOutputStream = new FileOutputStream(f); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(fileOutputStream); ObjectOutputStream objectOutputStream = new ObjectOutputStream(gzipOutputStream); objectOutputStream.writeObject(yourClassifier); objectOutputStream.flush(); objectOutputStream.close(); gzipOutputStream.close(); fileOutputStream.close();

答案 4 :(得分:0)

可以 生成所需的范围而不用检查,但你可能会面临内存不足,因为存储所有这些numbesr为15长的数字在列表中 - 是个坏主意。

更具体地说,您的代码将如下所示:

        long dig = (long) Math.pow(10, digits / 2);
        int pow = 10;
        int npow = 100;
        for (long i = 1; i <= dig; i++) {
            System.out.println(i * pow + inverse(i));
            System.out.println(i * pow / 10 + inverse(i / 10));
//            list.add(i * pow + inverse(i));
//            list.add(i * pow/10 + inverse(i / 10));
            if (i % pow == 0) {
                pow = npow;
                npow *= 10;
            }
        }

我故意评论列表添加行。

这个想法是将所有由给定的一半组成的数字推入列表/输出:

XXXY+YXXX

XXX+Y+XXX

即。产生两种情况:奇数和偶数回文。