Python 优化bigint调用

Python 优化bigint调用,python,optimization,d,Python,Optimization,D,我目前正在使用《D编程》一书来学习D。我试图解决一个问题,将1到10000000之间的数字平方相加。我首先用函数方法来解决map和reduce的问题,但是随着数字越来越大,我必须将数字转换为bigint以获得正确的输出 long num = 10000001; BigInt result; result = iota(1,num).map!(a => to!BigInt(a * a)).reduce!((a,b) => (a + b)); writeln("The s

我目前正在使用《D编程》一书来学习D。我试图解决一个问题,将1到10000000之间的数字平方相加。我首先用函数方法来解决map和reduce的问题,但是随着数字越来越大,我必须将数字转换为bigint以获得正确的输出

  long num = 10000001;
  BigInt result;
  result = iota(1,num).map!(a => to!BigInt(a * a)).reduce!((a,b) => (a + b));
  writeln("The sum is : ", result);
当使用dmd-O编译时,上述过程需要7秒才能完成。我分析了这个程序,大部分时间都浪费在BigInt调用上。虽然数字的平方可以放入一个长字符串中,但我必须将它们转换为bigint,以便减少函数和并返回适当的和。python程序只需3秒钟即可完成。当num=100000000时,程序将在1分13秒后完成。有没有办法优化对bigint的调用。产品本身可能很长,但它们必须被类型化为bigint对象,以便从reduce操作中得到正确的结果。我试着将数字的平方放入一个bigint数组,但速度也较慢。我试着把所有的数字都打成Bigint

auto bigs_map_nums = iota(1,num).map!(a => to!BigInt(a)).array;
auto bigs_map = sum(bigs_map_nums.map!(a => (a * a)).array);
但它的速度也较慢。我在学校读答案。D中较大整数的乘法实现也有问题吗?有没有办法优化对BigInt的函数调用

python代码:

timeit.timeit('print sum(map(lambda num : num * num, range(1,10000000)))',number=1)
333333283333335000000
3.58552622795105
该代码是在一台双核64位linux笔记本电脑上执行的,该笔记本电脑具有2 GB RAM。 python:2.7.4
dmd:DMD64 D编译器v2.066.1

dmd的后端不会发出高度优化的代码。对于快速程序,使用GDC或LDC编译

在我的计算机上,我得到以下计时信息:

Python:                    3.01
dmd   -O -inline -release: 3.92
ldmd2 -O -inline -release: 2.14

无范围冷却:
foreach(x;0..num)结果+=x*x

带范围冷却(?)度:

当然,关键是要避免
BigInt
ing每个元素

范围版本比非范围版本稍微慢一点。两者都比python版本快得多

编辑:哦!哦使用
std.algorithm.sum
,它会变得更加令人愉快:

result = iota(1, num)
    .map!(a => a * a)
    .sum(BigInt(0));

python代码并不等同于D代码,事实上它做的要少得多

Python使用一个int,然后当结果大于int()类型中可以存储的值时,它将该int提升为long()。在内部,(至少CPython)使用一个长数字来存储大于256的整数,即至少32位。在溢出之前,正常的cpu指令可以用于乘法,这比bigint乘法快得多

D的BigInt实现从一开始就将数字视为BigInt,并从1开始到结束使用昂贵的乘法运算。还有很多工作要做

有趣的是,当我们谈论bigint时,乘法是多么复杂

D实现是

Python从做开始

静态PyObject*
int_mul(PyObject*v,PyObject*w)
{
长a,b;
long longprod;/*a*b在本机long算术中*/
双双双_longprod;/*(双)longprod*/
双双针;/*(双针)a*(双针)b*/
将_转换为_LONG(v,a);
将_转换为_LONG(w,b);
/*下一行中的强制转换可避免溢出时的未定义行为*/
longprod=(长)((无符号长)a*b);
…//检查我们是否溢出了
{
const double diff=double_longprod-double prod;
常数双absdiff=diff>=0.0?diff:-diff;
常数double absprod=doubleprod>=0.0?doubleprod:
-双探针;

/*absdiff/absprod谢谢。随着num值的增加,计时变得越来越大,dmd需要1分钟15个sconds。除了编译器级别的更改外,是否可以对bigint调用进行任何优化?谢谢。请解释一下为什么使用bigint(0)在sum和reduce中。它如何使程序仅在需要时升级到BigInt?非常感谢。它使程序的速度比python快了7倍左右:)如果您能解释一下使用reduce和为什么使用反向参数的神奇之处,这将非常有帮助。sum版本看起来也非常优雅:)请添加一个l因为我是D新手,所以这里有更多的细节。但是在profiler中我可以看到bigint调用。这怎么会更快呢?非常感谢。有没有什么方法可以做类似的事情,只在必要的时候自动提升数字,比如D中的python?它依赖于实现吗?我不确定是否有自动/聪明的方法可以做到这一点,但如果我尝试一下,我会开始使用自定义类型DynInt,实现“接口”关于BigInt和mul,我会使用与python几乎相同的代码将存储值提升为BigInt谢谢。我接受了答案,该程序比以前的版本快7倍左右。但我无法掌握在sum和reverseArgs中BigInt的用法。我请求op添加解释,但无法获得。请解释一下,好吗.reverseArgs!(reduce!((a,b)=>a+b))(BigInt(0)/*seed*/)如果我们从内部读取它,我们专门化一个进行求和的reduce函数。然后对于reduce(a,b),我们通过reverseArgs将其更改为reduce(b,a)。但是,你可能会问,为什么?reduce原样需要(MapResult!(…某物),BigInt)作为参数,我们希望将BigInt作为第一个参数传递,以强制所有内容使用BigInt加法(通过种子)。为什么要将BigInt添加到它。为什么要反转参数。加法是可交换的。
result = iota(1, num)
    .map!(a => a * a)
    .sum(BigInt(0));