我在Python中定义了一个因子函数:
def fact(n):
if n == 1:
return n
else:
return n * fact(n-1)
print(fact(100))
如下朱莉娅:
function fact(n)
if n == 1
n
else
n * fact(n-1)
end
end
println(fact(100))
python程序返回一个非常大的数字,用于评估100(正如预期的那样)。 Julia返回0.使用较小的数字(如10)它们都可以工作。
我有两个问题:
答案 0 :(得分:20)
Julia有单独的固定大小整数类型,加上BigInt类型。默认类型为Int64
,当然是64位。
100以来!需要大约526位,它显然会溢出Int64
。
您只需执行fact(BigInt(100))
(假设您require
)就可以解决此问题,当然您也可以在fact
函数中进行转换。
int
,根据你的机器有16或32或64位,long
是任意长度的。fact(100L)
。如果您在Python 1.5上运行程序,它将像Julia一样包裹,或者引发异常。解决方案是致电long
,或在fact
函数内转换为int
。
但是,在2.x系列中的某些时候,Python将这两种类型绑定在一起,因此任何自动溢出的long
都会成为long
。然后,在3.0中,它完全合并了两种类型,因此不再有单独的int64
。
那么,为什么朱莉娅只是溢出而不是提出错误?
常见问题解答实际上解释了Why does Julia use native machine integer arithmetic。其中包括溢出时的环绕行为。
通过“原生机器算术”,人们通常意味着“C几乎在所有2s补充机器上做什么”。特别是在像Julia和Python这样的语言中,它们最初构建在C语言之上,并且非常接近金属。就朱莉娅而言,这不仅仅是一种“默认”,而是一种有意的选择。
在C中(至少和当时一样),实际上取决于实现如果溢出像OverflowError
这样的有符号整数类型会发生什么......但是几乎任何本地使用2的补码算法的平台上都是如此(这几乎是你今天看到的任何平台),完全相同的事情发生:它只是截断前64位以上的所有内容,这意味着你从正面到负面回绕。实际上, unsigned 整数类型 required 在C中以这种方式工作。(同时,C也是这样工作的,因为这是大多数CPU的工作方式。)
在C(与大多数CPU的机器语言不同)中,没有办法检测到事后你已经溢出了。因此,如果要引发int64
,则必须编写一些逻辑来检测乘法在执行之前是否会溢出。而且你必须在每一次乘法运行逻辑。您可以通过编写内联汇编代码为某些平台优化此功能。或者你可以转换为更大的类型,但是(a)往往会使你的代码变慢,并且(b)如果你已经使用了最大的类型(%
在许多平台上,它就不起作用了今天)。
在Python中,使每次乘法运算速度降低4倍(通常更少,但可能会更高)并不是什么大问题,因为Python花费更多时间获取字节码并取消整理对象而不是乘法。但朱莉娅意味着比这更快。
John Myles White在Computers are Machines中解释道:
在许多方面,朱莉娅通过尝试恢复从C语言转换为Python等语言时失去的一些力量,使自己与其他新语言脱颖而出。但转型带来了实质性的学习曲线。
但是还有另外一个原因:在许多情况下,溢出的带符号算法实际上很有用。不会像溢出无符号算术那样多(这就是为什么C在第一个ANSI规范之前定义了无符号算术的工作方式),但是有一些用例。
而且,即使你可能比想要翻转更频繁地想要类型转换,但是手动进行类型转换比翻转更容易 。如果您曾在Python中完成它,那么选择BigInt
的操作数并获得正确的符号肯定容易出错;施放到str
很难搞砸。
最后,在强类型语言中,如Python和Julia,类型稳定性很重要。 Python 3存在的原因之一是旧的unicode
类型神奇地转换为int
会导致问题。你的long
类型奇怪地转换为int
导致问题的情况要少得多,但它可能会发生(例如,当您从线路上获取值,或通过C API,并期望以相同的格式写出结果)。 Python的开发团队在进行long
/ {{1}}统一时引用了这一点,引用了“实用性超越纯度”和Zen的其他各种内容,并最终决定旧行为导致的问题多于新行为将。朱莉娅的设计做出了相反的决定。
答案 1 :(得分:16)
没人回答为什么朱莉娅的结果是0。
Julia不检查溢出的整数乘法,因此64位整数的乘法是执行mod 2 ^ 63。见this FAQ entry
当你写出阶乘的乘法时,你得到
1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20*21*22*23*24*25*26*27*28*29*30*31*32*33*34*35*36*37*38*39*40*41*42*43*44*45*46*47*48*49*50*51*52*53*54*55*56*57*58*59*60*61*62*63*64*65*66*67*68*69*70*71*72*73*74*75*76*77*78*79*80*81*82*83*84*85*86*87*88*89*90*91*92*93*94*95*96*97*98*99*100
这也可以写为主要因素
2^97 * 3^48 * 5^24 * 7^16 * 11^9 * 13^7 * 17^5 * 19^5 * 23^4 * 29^3 * 31^3 * 37^2 * 41^2 * 43^2 * 47^2 * 53^1 * 59^1 * 61^1 * 67^1 * 71^1 * 73^1 * 79^1 * 83^1 * 89^1 * 97^1
如果您查看2
的指数,则会得到97
。模块化算术使您可以在计算的任何步骤执行mod函数,并且不会影响结果。与链的其余部分相乘的2^97 mod 2^63 == 0
也为0.
<强>更新强> 我当然懒得在纸上做这个计算。
d = Dict{Int,Int}()
for i=1:100
for (k,v) in factor(i)
d[k] = get(d,k,0) + v
end
end
for k in sort(collect(keys(d)))
print("$k^$(d[k])*")
end
Julia在其标准库中有一个非常方便的factor()函数。
答案 2 :(得分:10)
我想答案是使用BigInt
:
function fact(n::BigInt)
if n == BigInt(1)
n
else
n * fact(n-BigInt(1))
end
end
println(fact(BigInt(100)))
结果如下:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
经过测试:http://forio.com/julia/repl/
正如其他一些答案中所述,Python会隐式地将超过最大大小的int(s)转换为bigint(s),因此您可以获得预期的结果,而不是默默地失败。
另一方面,朱莉娅似乎更明确地表达了这一点,并赞成表现优于“预期行为”。 Julia是一种带有OPTIONal Type Annotations和Inference的动态语言。答案 3 :(得分:2)
Python自动使用可容纳任意大数的BigInt。在朱莉娅,你必须自己做。我认为它会像这样纠正
function fact(n::BigInt)
if n == 1
n
else
n * fact(n-1)
end
end
println(fact(BigInt(100)))
答案 4 :(得分:0)
朱莉娅快速的原因之一是它们避免了会影响性能的功能。这是其中之一。在Python中,解释程序不断检查它是否应该自动切换到BigInt库。不断检查是有代价的。
这是一个可以做你想要的功能:
function fact(n)
if n == 0
1
else
big(n) * fact(n-1)
end
end
println( fact(100) )
println( fact(0) )
我冒昧地纠正了程序中的错误:定义了零因子,并且是1.顺便说一下,你可以像这样编写你的函数:
function !(n)
if n == 0
1
else
big(n) * !(n-1)
end
end
println( !(100) )
println( !(0) )
我不会亲自这样做,因为“foo!”函数通常用于修改参数的函数。但我想提供选择。最后,我无法抗拒提供单行替代方案:
fact(n) = n == 0 ? 1 : big(n) * !(n-1)
println( fact(100) )
println( fact(0) )
答案 5 :(得分:0)
顺便说一句,我认为在Julia中这样做的惯用方法是利用类型系统为不同类型编译不同的版本:
fact(n) = n <= zero(n) ? one(n) : n*fact(n-one(n))
# one(n) gives you a one, as it were, of the same type as n
然后,根据输入的类型编译和调用该函数的不同版本,用户必须决定使用哪种类型,以及调用哪个版本的函数:
julia> fact(10)
3628800
julia> fact(100)
0
julia> fact(BigInt(100))
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
当我们查看Int64的(LLVM)编译版本与BigInt事实()时,可以看到BigInt vs machine(Int64)算法的开销,由@abarnert很好地描述:
julia> code_llvm(fact,(Int64,))
define i64 @"julia_fact;23421"(i64) {
top:
%1 = icmp sgt i64 %0, 0, !dbg !10800
br i1 %1, label %L, label %if, !dbg !10800
if: ; preds = %top
ret i64 1, !dbg !10800
L: ; preds = %top
%2 = add i64 %0, -1, !dbg !10800
%3 = call i64 @"julia_fact;23398"(i64 %2), !dbg !10800
%4 = mul i64 %3, %0, !dbg !10800
ret i64 %4, !dbg !10800
}
julia> code_llvm(fact,(BigInt,))
define %jl_value_t* @"julia_fact;23422"(%jl_value_t*, %jl_value_t**, i32) {
top:
%3 = alloca [6 x %jl_value_t*], align 8
%.sub = getelementptr inbounds [6 x %jl_value_t*]* %3, i64 0, i64 0
%4 = getelementptr [6 x %jl_value_t*]* %3, i64 0, i64 2, !dbg !10803
store %jl_value_t* inttoptr (i64 8 to %jl_value_t*), %jl_value_t** %.sub, align 8
%5 = load %jl_value_t*** @jl_pgcstack, align 8, !dbg !10803
%6 = getelementptr [6 x %jl_value_t*]* %3, i64 0, i64 1, !dbg !10803
%.c = bitcast %jl_value_t** %5 to %jl_value_t*, !dbg !10803
store %jl_value_t* %.c, %jl_value_t** %6, align 8, !dbg !10803
store %jl_value_t** %.sub, %jl_value_t*** @jl_pgcstack, align 8, !dbg !10803
store %jl_value_t* null, %jl_value_t** %4, align 8, !dbg !10803
%7 = getelementptr [6 x %jl_value_t*]* %3, i64 0, i64 3
store %jl_value_t* null, %jl_value_t** %7, align 8
%8 = getelementptr [6 x %jl_value_t*]* %3, i64 0, i64 4
store %jl_value_t* null, %jl_value_t** %8, align 8
%9 = getelementptr [6 x %jl_value_t*]* %3, i64 0, i64 5
store %jl_value_t* null, %jl_value_t** %9, align 8
%10 = load %jl_value_t** %1, align 8, !dbg !10803
%11 = call %jl_value_t* @julia_BigInt2(i64 0), !dbg !10804
store %jl_value_t* %11, %jl_value_t** %4, align 8, !dbg !10804
%12 = getelementptr inbounds %jl_value_t* %10, i64 1, i32 0, !dbg !10804
%13 = getelementptr inbounds %jl_value_t* %11, i64 1, i32 0, !dbg !10804
%14 = call i32 inttoptr (i64 4535902144 to i32 (%jl_value_t**, %jl_value_t**)*)(%jl_value_t** %12, %jl_value_t** %13), !dbg !10804
%15 = icmp sgt i32 %14, 0, !dbg !10804
br i1 %15, label %L, label %if, !dbg !10804
if: ; preds = %top
%16 = call %jl_value_t* @julia_BigInt2(i64 1), !dbg !10804
%17 = load %jl_value_t** %6, align 8, !dbg !10804
%18 = getelementptr inbounds %jl_value_t* %17, i64 0, i32 0, !dbg !10804
store %jl_value_t** %18, %jl_value_t*** @jl_pgcstack, align 8, !dbg !10804
ret %jl_value_t* %16, !dbg !10804
L: ; preds = %top
store %jl_value_t* %10, %jl_value_t** %7, align 8, !dbg !10804
store %jl_value_t* %10, %jl_value_t** %8, align 8, !dbg !10804
%19 = call %jl_value_t* @julia_BigInt2(i64 1), !dbg !10804
store %jl_value_t* %19, %jl_value_t** %9, align 8, !dbg !10804
%20 = call %jl_value_t* @"julia_-;23402"(%jl_value_t* inttoptr (i64 140544121125120 to %jl_value_t*), %jl_value_t** %8, i32 2), !dbg !10804
store %jl_value_t* %20, %jl_value_t** %8, align 8, !dbg !10804
%21 = call %jl_value_t* @"julia_fact;23400"(%jl_value_t* inttoptr (i64 140544559367232 to %jl_value_t*), %jl_value_t** %8, i32 1), !dbg !10804
store %jl_value_t* %21, %jl_value_t** %8, align 8, !dbg !10804
%22 = call %jl_value_t* @"julia_*;23401"(%jl_value_t* inttoptr (i64 140544121124768 to %jl_value_t*), %jl_value_t** %7, i32 2), !dbg !10804
%23 = load %jl_value_t** %6, align 8, !dbg !10804
%24 = getelementptr inbounds %jl_value_t* %23, i64 0, i32 0, !dbg !10804
store %jl_value_t** %24, %jl_value_t*** @jl_pgcstack, align 8, !dbg !10804
ret %jl_value_t* %22, !dbg !10804
}