I improved the benchmarking code by reducing the number of cube
computations. Interestingly, this gives a huge improvement of guile-2.9.1
performance while improving less or even worsening performance for other
scheme interpreters I've tested.

This could indicate that

1. the guile-2.9.1 optimizer is able to convert the extra code I introduced
to avoid unnecessary cube computation (let:s and extra args) to something
with low overhead
2. guile-2.9.1 arithmetic is a bit heavy

Anyhow, now guile-1.8 went up to 7.53 s, guile-2.9.1 went down to 0.53 s
while python went down to 3.60 s.

Attaching version 2 of the benchmarks.

(BTW, on my machine the previous version is better at provoking a segfault.)
;;;; ramanujan.scm -- Compute the N:th Ramanujan number
;;;;
;;;; Copyright (C) 2018 Mikael Djurfeldt
;;;;
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 3, or (at your option)
;;;; any later version.
;;;; 
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;; GNU General Public License for more details.
;;;; 
;;;; You should have received a copy of the GNU General Public License
;;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
;;;;

;;; Version 2

(define (ramanujan n)
  "Return the N:th Ramanujan number (sum of two cubes in more than one way)"
  
  (define (ramanujan? w b0)
    ;; Is w a Ramanujan number?
    (let loop ((a 1) (a3 1) (b b0) (b3 (* b0 b0 b0)) (seen #f))
      (and (<= a b)
	   (let ((s (+ a3 b3)))
	     (cond ((< s w) (let ((a1 (+ 1 a))) ; too small => inc a
			      (loop a1 (* a1 a1 a1) b b3 seen)))
		   ((> s w) (let ((b1 (- b 1))) ; too large => dec b
			      (loop a a3 b1 (* b1 b1 b1) seen)))
		   (else (let ((b1 (- b 1))) ; found a sum!
			   (or seen
			       (loop a a3 b1 (* b1 b1 b1) #t)))))))))
  
  (define (iter w b0 n)
    ;; w is a Ramanujan candidate
    ;; b0 is the first second term to try
    ;; n is the number of Ramanujan number still to find

    ;; We first increase b0 until 1 + b0^3 >= w
    (let ((b0 (let loop ((b b0))
		(if (>= (+ 1 (* b b b)) w)
		    b
		    (loop (+ 1 b))))))
      
      (cond ((zero? n) (- w 1)) ; found the last number!
	    ((ramanujan? w b0) (iter (+ 1 w) b0 (- n 1)))
	    (else (iter (+ 1 w) b0 n))))) ; try next candidate
  
  (iter 2 1 n))
#!/usr/bin/python3

#  ramanujan.py -- Compute the N:th Ramanujan number
#  
#  Copyright (C) 2018 Mikael Djurfeldt
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# Version 2

# return the N:th Ramanujan number (sum of two cubes in more than one way)
#
def ramanujan (n):
    w = 0 # Ramanujan number candidate
    b0 = 1 # first second term to try
    while n > 0:
        w += 1 # try next candidate

        # increase initial b0 until 1 + b0^3 >=w
        while 1 + b0 * b0 * b0 < w:
            b0 += 1
            
        a = 1
        a3 = 1
        b = b0
        b3 = b0 * b0 * b0
        count = 0 # number of ways to write w
        while a <= b:
            s = a3 + b3
            if s < w:
                a += 1 # if sum is too small, increase a
                a3 = a * a * a
                continue
            elif s == w:
                count += 1 # found a sum!
                if count > 1:
                    n -= 1
                    break
            b -= 1 # increase b both if sum too large and to find next way to write w
            b3 = b * b * b
    return w

Reply via email to