提高将数字转换为列表的性能,以及将base10转换为base2的性能

许多Project Euler问题需要在base10和base2中操纵整数及其数字.虽然我在数字列表中转换整数或将base10转换为base2(或其数字列表)没有问题,但我经常发现重复进行此类转换时性能很差.

这是一个例子:

首先,这是我的典型转换:

#lang racket
(define (10->bin num)
  (define (10->bin-help num count)
    (define sq
      (expt 2 count))
    (cond
      [(zero? count) (list num)]
      [else (cons (quotient num sq) (10->bin-help (remainder num sq) (sub1 count)))]
      )
    )
  (member 1 (10->bin-help num 19)))

(define (integer->lon int)
  (cond
    [(zero? int) empty]
    [else (append (integer->lon (quotient int 10)) (list (remainder int 10)))]
    )
  )

接下来,我需要一个函数来测试数字列表是否是回文

(define (is-palindrome? lon)
  (equal? lon (reverse lon)))

最后,我需要将所有base10整数加在base2和base10的最大值下,这些是palindromes.这是累加器式函数:

(define (sum-them max)
  (define (sum-acc count acc)
    (define base10
      (integer->lon count))
    (define base2
      (10->bin count))
    (cond
      [(= count max) acc]
      [(and
           (is-palindrome? base10)
           (is-palindrome? base2))
          (sum-acc (add1 count) (+ acc count))]
         [else (sum-acc (add1 count) acc)]))
  (sum-acc 1 0))

和常规递归版本:

(define (sum-them* max)
  (define base10
    (integer->lon max))
  (define base2
    (10->bin max))
  (cond
    [(zero? max) 0]
    [(and
      (is-palindrome? base10)
      (is-palindrome? base2))
     (+ (sum-them* (sub1 max)) max)]
    [else (sum-them* (sub1 max))]
    )
  )

当我将这两个最后一个函数中的任何一个应用到1000000时,我需要10秒以上才能完成.递归版本似乎比累加器版本快一点,但差异可以忽略不计.

有什么方法可以改进这个代码,或者我只是必须接受这是数字运算的风格,而Racket并不是特别适合?

到目前为止,我已经考虑过用类似的整数 – >向量替换整数 – > lon的可能性,因为我期望vector-append比追加更快,但是后来我仍然需要应用反向.

最佳答案
使您现有的代码更有效率

您是否考虑过使用Racket bitwise operations中的任何一个来获取位列表?例如.,

(define (bits n)
  (let loop ((n n) (acc '()))
    (if (= 0 n) 
        acc
        (loop (arithmetic-shift n -1) (cons (bitwise-and n 1) acc)))))
> (map bits '(1 3 4 5 7 9 10))
'((1) (1 1) (1 0 0) (1 0 1) (1 1 1) (1 0 0 1) (1 0 1 0))

有趣的是看是否能加快速度.我希望它会有所帮助,因为你的10-> bin过程当前会调用expt,quotient和remainder,而位移,取决于编译器使用的表示,可能会更有效.

你的整数 – > lon也使用了比你需要的更多的内存,因为append正在复制每一步的大部分结果.这很有意思,因为你已经在bin-> 10中使用了更高效的内存方法.这样的事情更有效:

(define (digits n)
  (let loop ((n n) (acc '()))
    (if (zero? n)
        acc
        (loop (quotient n 10) (cons (remainder n 10) acc)))))
> (map digits '(1238 2391 3729))
'((1 2 3 8) (2 3 9 1) (3 7 2 9))

更有效的方法

所有这些,也许你应该考虑你正在使用的方法.现在看来,你正在遍历数字1 … MAX,检查每一个是否是回文,如果是,则将它加到总和中.这意味着你正在使用MAX数字做一些事情.而不是检查回文数字,为什么不直接在一个基地生成它们,然后检查它们是否是另一个的回文.即,而不是检查1 … MAX,检查:

> 1
> 11
> 101和111
> 1001和1111
> 10001,10101,11011和11111,
>等等,直到数字太大.

这个列表是所有二元回文,只有一些是十进制回文.如果你可以使用bit-twiddling技术生成二进制回文(所以你实际上正在使用整数),很容易将它们写入字符串,并检查字符串是否是回文可能比检查列表要快得多是一个回文.

转载注明原文:提高将数字转换为列表的性能,以及将base10转换为base2的性能 - 代码日志