首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Raku列表理解

Raku列表理解
EN

Stack Overflow用户
提问于 2017-02-22 12:39:16
回答 3查看 707关注 0票数 9

我是在Windows 10 i7第4代笔记本电脑8 GB内存。

我想找出从1到1000000000的数字之和,可除以5。

我试图在Raku REPL中运行这段代码:

代码语言:javascript
运行
复制
($_ if $_%5==0 for 1..1000000000).sum

代码运行了45分钟,仍然没有输出。我怎样才能克服它?

在这种情况下,如何应用并发呢?我认为上面的问题可以通过并发或任务并行来解决!!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-02-22 18:34:04

这很慢,因为你想要还原一组物品。作为另一种选择,您可以构造一个序列:(5, 10 … 1000000000).sum,但这仍然在大量元素周围进行整合和保持,所以它仍然很慢。您可以做一个C风格的循环,并为每个增量添加和,但这并不有趣(对于足够大的数字,这仍然缓慢)。

你可以用数学来解决这个问题:可以被N整除的数字是它的倍数,如果我们把N从这个序列中剔除,我们会发现你要寻找的所有数字的和都是N * (1 + 2 + ... + floor 1000000000/N)。因为这是一个连续的范围在那里,我们可以使用它的Range.sum (它知道如何快速完成)来计算该部分。因此,我们得到:

代码语言:javascript
运行
复制
sub sum-of-stuff (\n, \d) { d * sum 1..n div d; }
say sum-of-stuff 1000000000, 5 # OUTPUT: 100000000500000000

因此,这是最快和最明智的方法来计算你的问题,但这不是最有趣的!

您提到了并发性,所以让我们来讨论一下这个方法。我们的问题是将物质重新化,所以我们需要想出一种方法,用我们现有的核数来把我们原来的数字范围分块,然后再对每个单独的核心进行放大和寻找乘法器的工作。然后我们将总结每个核心中每个块中的内容,然后回到主线程,总结各块的总和,以得到最终的答案。在代码中,应该是这样的:

代码语言:javascript
运行
复制
constant N = 10000;
constant DIV = 5;
constant CORES = 32;
constant batch = N div CORES;

(0, batch … N, N).rotor(2 => -1).flat.map({$^a^..$^b})
    .race(:batch :degree(CORES)).map(*.grep(* %% DIV).sum).sum.say;

batch是每个核心需要处理的块的大小,下面是为每一位完成所有工作的一行代码的解释:

我们使用序列操作符来创建一个0, batch, 2*batch, 3*batch序列,等等,直到N。由于我们希望N成为它的一部分,所以我们第二次将它包括在内:

代码语言:javascript
运行
复制
(0, batch … N, N)

我们现在想要的是使用该序列来创建一组Range对象,我们希望重用序列中的元素,所以我们使用批处理2和后退1的.rotor,然后扁平子列表并使用.map来创建范围对象(.map: *^..*看起来好多了,但是遗憾的是,无论星体不想在这种安排中得到什么):

代码语言:javascript
运行
复制
.rotor(2 => -1).flat.map({$^a^..$^b})

现在是有趣的地方,我们使用.race方法来创建一个HyperSeq,以便使用我们所有的核心来评估它。它的:batch命名参数允许您指定每个批处理多少个元素,它的:degree指定要使用多少个线程。我们已经对数据进行了批量处理,因此对于:batch,我们使用了1。对于:degree,我们使用我们的核数。为什么我们不让它把我们的东西分批?因为具体化是我们的敌人,我们想要在不同的线上具体化。让它分批处理会使所有的内容在一个线程中具体化,而不是:

代码语言:javascript
运行
复制
.race(:batch :degree(CORES))

所以现在我们已经掌握了我们的HyperSeq,我们可以在上面绘制地图。每个项目都是我们的Range对象大小为一批,召回。所以我们在上面调用.grep,寻找可以被我们想要的除数整除的数字,然后调用.sum

代码语言:javascript
运行
复制
.map(*.grep(* %% DIV).sum)

在顶部的最后一个樱桃,我们想要总结每个块的和,并打印结果,所以我们调用

代码语言:javascript
运行
复制
.sum.say;

塔达!

您也可以用这种方式重写有趣的部分,并使用承诺而不是.race

代码语言:javascript
运行
复制
say sum await (0, batch … N, N).rotor(2 => -1).flat.map:
    { start sum grep * %% DIV, $^a^..$^b }

很相似,也有点短。以前为我们创建Range的映射现在也激发了一个承诺(使用start关键字),它对块进行打招呼和求和。在行的开头,我们添加了await,等待所有承诺的结果,然后总结结果。

这仍然是缓慢的地狱,不会帮助你原来的问题,因此,为什么你应该使用一个好的阿尔戈;)

干杯。

票数 11
EN

Stack Overflow用户

发布于 2017-02-22 18:07:42

我怎样才能克服它?

通过选择更好的算法:

my \N = 1_000_000_000。你对这个价值感兴趣

代码语言:javascript
运行
复制
[+] grep * %% 5, 1..N

这和

代码语言:javascript
运行
复制
[+] map * * 5, 1..N/5

这反过来

代码语言:javascript
运行
复制
5 * [+] 1..N/5

Rakudo足够聪明,可以在固定时间内将一个范围相加,您将立即得到结果(几乎)。

票数 18
EN

Stack Overflow用户

发布于 2017-02-22 16:39:43

1_000_000_000说,这需要多长时间才能得到比1_000_000低的值?在我的机器上大约需要3秒。由此推断,1000倍的值需要检查,这意味着大约需要3000秒。

为了使事情更快,您可以使用%% (可以被)运算符,而不是%(模块化):

代码语言:javascript
运行
复制
($_ if $_ %% 5 for 1..1000000000).sum

我还以为这会更快呢。我会考虑使%%更快,但我担心你仍然会看你的算法的小时规模的时间。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42391754

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档