生成百万随机元素的列表

时间:2010-03-13 08:09:51

标签: scheme

如何有效地生成方案中的百万随机元素列表?以下代码以0.1百万本身命中最大递归深度。

(unfold (lambda(x)(= x 1000000)) (lambda(x)(random 1000)) (lambda(x)(+ x 1)) 0)

6 个答案:

答案 0 :(得分:6)

这实际上取决于您使用的系统,但这是在普通方案中执行此操作的常用方法:

(let loop ([n 1000000] [r '()])
  (if (zero? n)
    r
    (loop (- n 1) (cons (random 1000) r))))

关于运行此代码的一个注意事项是:如果您只是将其键入REPL,它将导致打印结果列表,这通常会涉及使用比列表保留更多的内存。所以最好做一些像

这样的事情
(define l ...same...)

还有许多其他工具可用于不同程度的便利性。 unfold是其中之一,另一个是for循环,可以在PLT Scheme中找到:

(for/list ([i (in-range 1000000)]) (random 1000))

答案 1 :(得分:4)

我不知道多少方案,但你不能只使用尾递归(它实际上只是循环)而不是展开(或任何其他高阶函数)?

答案 2 :(得分:2)

使用do-loop-construct as described here

答案 3 :(得分:2)

如果我错了,有人会纠正我但是Fakrudeen的代码应该最终被优化掉,因为它是尾递归的。或者应该正确实现展开。它永远不会达到最大递归深度。

您使用Fakrudeen的是什么版本的计划? DrScheme并没有扼杀几百万个随机数字。

答案 4 :(得分:2)

将Chicken-Scheme作为实施,这是尝试一些结果。

(use srfi-1)
(use extras)

(time (unfold (lambda(x)(= x 1000000))
              (lambda(x)(random 1000))
              (lambda(x)(+ x 1)) 0))

(time (let loop ([n 1000000] [r '()])
        (if (zero? n)
            r
            (loop (- n 1) (cons (random 1000) r)))))

(define (range min max body)
  (let loop ((current min) (ret '()))
    (if (= current max)
      ret
      (loop (+ current 1) (cons (body current ret) ret)))))

(time (range 0 1000000 (lambda params (random 1000))))

结果显示在csc -O3 t.scm

0.331s CPU time, 0.17s GC time (major), 12/660 GCs (major/minor)
0.107s CPU time, 0.02s GC time (major), 1/290 GCs (major/minor)
0.124s CPU time, 0.022s GC time (major), 1/320 GCs (major/minor)

正如您所看到的,作者的版本比使用普通尾递归调用要慢得多。很难说为什么展开调用要慢得多,但我猜这是因为它花了很多时间进行函数调用。

其他两个版本非常相似。我的版本几乎是一样的,除了我正在创建一个可以重用的高阶函数。

与普通循环不同,它可以重复使用以创建一系列功能。位置和当前列表将在需要时发送给函数。

高阶版本可能是最好的方法,即使它需要更多的时间来执行。这可能也是因为函数调用。它可以通过删除参数进行优化,并且几乎与命名的let一样快。

高阶版本的优点是用户不必自己编写循环,可以与抽象的lambda函数一起使用。

修改

看看这个具体案例。如果我们要创建一个介于0到999之间的百万元素,我们可以创建一个百万的固定长度向量,其值为0到999。之后洗了回来的东西。然后整个随机过程将取决于shuffle函数,该函数不应该创建新的内存交换值可能比生成随机数更快。也就是说,shuffle方法在某种程度上仍然依赖于随机。

编辑2

除非你真的需要一个清单,否则你可以改为使用矢量。

以下是我使用vector-map

的第二个实现
(time (vector-map (lambda (x y) (random 1000)) (make-vector 1000000)))
# 0.07s CPU time, 0/262 GCs (major/minor)

如您所见,它比使用列表快得多。

编辑3个乐趣

(define-syntax bigint
  (er-macro-transformer
    (lambda (exp rename compare)
      (let ((lst (map (lambda (x) (random 1000)) (make-list (cadr exp)))))
        (cons 'list lst)))))

100000
0.004s CPU time, 0/8888 GCs (major/minor)

使用它可能不是一个好主意,但我觉得它可能很有趣。由于它是一个宏,它将在编译时执行。编译时间很长,但正如您所看到的,速度提升也很大。不幸的是,使用鸡肉,我无法建立一个百万的名单。我的猜测是它可能用于构建列表的类型是溢出并访问无效的内存。

回答评论中的问题:

我不是计划专业人士。我也是新手,据我所知,命名循环或高阶函数应该是可行的方法。高阶函数很好,因为它是可重用的。你可以定义一个

(define (make-random-list quantity maxran)
 ...)

然后那是另一个有趣的部分,因为scheme是关于高阶函数的。然后,您可以用您喜欢的任何内容替换make-random-list的实现。如果需要一些编译时执行,请定义宏,否则使用函数。真正重要的是能够重复使用它。它必须快速而不是使用内存。

常识告诉你,执行较少会更快,尾递归调用不会消耗内存。当您不确定时,可以将实现隐藏到可以在以后优化的函数中。

答案 5 :(得分:1)

MIT Scheme限制计算的堆栈。鉴于您的问题的大小,您可能会超出堆栈大小。幸运的是,您可以提供命令行选项来更改堆栈大小。尝试:

$ mit-scheme --stack <number-of-1024-word-blocks>

还有其他命令行选项,请查看mit-scheme --help

请注意,根据我的经验,MIT Scheme是少数具有有限堆栈大小的方案之一。这解释了为什么在其他方案中尝试代码通常会成功。

关于你的效率问题。例程unfold可能没有使用尾递归/迭代算法实现。这是一个尾递归版本,尾递归版本的'list reverse in in place':

(define (unfold stop value incr n0)
  (let collecting ((n n0) (l '()))
    (if (stop n)
        (reverse! l)
        (collecting (incr n) (cons (value n) l)))))

(define (reverse! list)
  (let reving ((list list) (rslt '()))
    (if (null? list)
        rslt
        (let ((rest (cdr list)))
          (set-cdr! list rslt)
          (reving rest list)))))

注意:

$ mit-scheme --version
MIT/GNU Scheme microcode 15.3
Copyright (C) 2011 Massachusetts Institute of Technology
This is free software; see the source for copying conditions. There is NO warranty; not even
for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Image saved on Tuesday November 8, 2011 at 10:45:46 PM
  Release 9.1.1 || Microcode 15.3 || Runtime 15.7 || SF 4.41 || LIAR/x86-64 4.118 || Edwin 3.116

Moriturus te saluto.