重慶分公司,新征程啟航
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊(cè)、服務(wù)器等服務(wù)
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊(cè)、服務(wù)器等服務(wù)
python中try...except函數(shù)可以用if..else函數(shù)代替。在while循環(huán)內(nèi)部,fast指針每次向前走兩步,這時(shí)候我們就要判斷fast的next指針是否為None,不然對(duì)fast.next再調(diào)用next指針的時(shí)候就會(huì)報(bào)異常,這個(gè)異常出現(xiàn)也反過(guò)來(lái)說(shuō)明鏈表不存在環(huán),就可以returnFalse。所以可以把while代碼放到一個(gè)try…except中,一旦出現(xiàn)異常就return。這是一個(gè)比較好的思路,在以后寫(xiě)代碼的時(shí)候可以考慮替換某些if…else語(yǔ)句減少不必要的判斷,也使得代碼變的更簡(jiǎn)潔。
創(chuàng)新互聯(lián)主要從事成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)連山,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專(zhuān)業(yè),歡迎來(lái)電咨詢(xún)建站服務(wù):028-86922220
for ... in ...
//?for?...?in?字符串
for?i?in?'abc':
print(i)
'''
a
b
c
'''
//?for?...?in?數(shù)組
for?i?in?['a',?'b',?'c']:
print(i)
'''
a
b
c
'''
//?for?...?in?元組
for?i?in?('a',?'b',?'c'):
print(i)
'''
a
b
c
'''
//?for?...?in?字典(得到的是字典的key)
for?k?in?{'學(xué)號(hào)':30,'姓名':'小明'}:
print(k)
'''
姓名
學(xué)號(hào)
'''
//?for?...?in?字典.items()(得到的是字典的key,?value)
for?k,?v?in?{'學(xué)號(hào)':30,'姓名':'小明'}.items():
print(k,?v)
'''
姓名?小明
學(xué)號(hào)?30
'''
for ... in range(...)
//?for?...?in?range(num)
for?i?in?range(3):
print(i)
'''
1
2
'''
//?for?...?in?range(num1,?num2)
for?i?in?range(1,?3):
print(i)
'''
1
2
'''
//?for?...?in?range(num1,?num2,?num3)
for?i?in?range(3,?1,?-1):
print(i)
'''
3
2
'''
for?i?in?range(1,?5,?2):
print(i)
'''????????
1
3
'''
倒敘
for?i?in?reversed([1,2,3,4]):
print(i)
’‘’
4
3
2
1
‘’‘
又想得到遍歷次數(shù),又想得到數(shù)組值
for i,e in enumerate(array):
for?i,e?in?enumerate(['21dw',?55,?22,?'rw']):
print(i,?e)
'''
0?21dw
1?55
2?22
3?rw
'''
列表生成式
[...for ... in array]]
print([x?*?x?for?x?in?[2,?34,?324,?43]])
'''
[4,?1156,?104976,?1849]
'''
//相當(dāng)于
list?=?[]
for?x?in?[2,?34,?324,?43]:
list.append(x?*?x)
print(list)
因?yàn)樵贛athematica中使用循環(huán)確實(shí)是低效的。。。。。。
深層次的原因涉及到Mathematica的底層實(shí)現(xiàn)所以我不太懂,但是至少?gòu)南旅鎺讉€(gè)例子可以看出Mathematica里確實(shí)有很多比循環(huán)更好的方法
求和
首先舉一個(gè)最簡(jiǎn)單的求和例子,求的值。為了測(cè)試運(yùn)行時(shí)間取n=10^6
一個(gè)剛接觸Mathematica的同學(xué)多半會(huì)這樣寫(xiě)
sum = 0;
For[i = 1, i = 10^6, i++,
sum += Sin[N@i]];
(*其中N@i的作用是把整數(shù)i轉(zhuǎn)化為浮點(diǎn)數(shù),類(lèi)似于C里的double*)
sum
為了便于計(jì)時(shí)用Module封裝一下,運(yùn)行時(shí)間是2.13秒,如下圖
然后一個(gè)有一定Mathematica經(jīng)驗(yàn)的同學(xué)多半會(huì)知道同樣作為循環(huán)的Do速度比For快,于是他可能會(huì)這么寫(xiě)
然后一個(gè)有一定Mathematica經(jīng)驗(yàn)的同學(xué)多半會(huì)知道同樣作為循環(huán)的Do速度比For快,于是他可能會(huì)這么寫(xiě)
sum = 0;
Do[sum += Sin[N@i], {i, 1, 10^6}];
sum
如下圖,用時(shí)1.37秒,比For快了不少
當(dāng)然了知道Do速度比For快的同學(xué)不太可能不知道Sum函數(shù),所以上面其實(shí)是我口胡的,他應(yīng)該會(huì)這么寫(xiě)
Sum[Sin[N@i], {i, 1, 10^6}]
如下圖,同樣的結(jié)果,只用了不到0.06秒
如果這位同學(xué)還知道Listable屬性并且電腦內(nèi)存不算太小的話(huà),他也可能會(huì)這么寫(xiě)
Tr@Sin[N@Range[10^6]]
如下圖,只用了不到0.02秒,速度超過(guò)For循環(huán)的100倍
當(dāng)然了這只是一個(gè)最簡(jiǎn)單的例子,而且如果數(shù)據(jù)量更大的話(huà)最后一種方法就不能用了。但是這也足以說(shuō)明在求和時(shí)用循環(huán)是低效的,無(wú)論是內(nèi)置的Sum函數(shù)還是向量化運(yùn)算,在效率上都遠(yuǎn)遠(yuǎn)高于循環(huán)
(這部分模仿了不同程序員如何編寫(xiě)階乘函數(shù)這篇文章,強(qiáng)烈推薦對(duì)Mathematica有興趣的同學(xué)去看看)
迭代
接下來(lái)舉一個(gè)迭代的例子,(即Logistic map),取,為了測(cè)試運(yùn)行時(shí)間同樣取n=10^6
還是先用For循環(huán)的做法
x = 0.5;
For[i = 1, i = 10^6, i++,
x = 3.5 x (1 - x);
];
x
如下圖,運(yùn)行時(shí)間2.06秒
(Do循環(huán)和For類(lèi)似,篇幅所限這里就不寫(xiě)了,有興趣的同學(xué)可以自行嘗試)
(Do循環(huán)和For類(lèi)似,篇幅所限這里就不寫(xiě)了,有興趣的同學(xué)可以自行嘗試)
然后看看內(nèi)置的Nest函數(shù)
Nest[3.5 # (1 - #) , 0.5, 10^6]
如下圖,用時(shí)0.02秒,又是將近兩個(gè)數(shù)量級(jí)的效率差異
當(dāng)然了Nest的使用涉及到純函數(shù),對(duì)于Mathematica初學(xué)者來(lái)說(shuō)可能有些難以理解,而且一些比較復(fù)雜的迭代不太容易寫(xiě)成Nest的形式,但是在迭代時(shí)Nest(還包括Fold)的效率確實(shí)要好于循環(huán)
當(dāng)然了Nest的使用涉及到純函數(shù),對(duì)于Mathematica初學(xué)者來(lái)說(shuō)可能有些難以理解,而且一些比較復(fù)雜的迭代不太容易寫(xiě)成Nest的形式,但是在迭代時(shí)Nest(還包括Fold)的效率確實(shí)要好于循環(huán)
遍歷列表
依然舉一個(gè)簡(jiǎn)單的例子:求一個(gè)列表中偶數(shù)的個(gè)數(shù)。為測(cè)試生成10^6個(gè)1到10之間的隨機(jī)整數(shù)
list = RandomInteger[{1, 10}, 10^6];
(*生成10^6個(gè)隨機(jī)整數(shù)*)
如果用For循環(huán)的話(huà)代碼是這樣的
num = 0;
For[i = 1, i = 10^6, i++,
If[EvenQ@list[[i]], num++]
];
num
如下圖,用時(shí)1.73秒
保留上面的思路,單純的將For循環(huán)改為Scan (相當(dāng)于沒(méi)有返回結(jié)果的Map),代碼如下
num = 0;
Scan[If[EvenQ@#, num++] , list];
num
如下圖,用時(shí)0.91 秒
(Do循環(huán)用時(shí)1.00秒左右,篇幅所限就不傳圖了)
摒棄循環(huán)的思路,用其他內(nèi)置函數(shù)寫(xiě)
Count[list, _?EvenQ] // AbsoluteTiming
(*直接用Count數(shù)出list中偶數(shù)的個(gè)數(shù)*)
Count[EvenQ /@ list, True] // AbsoluteTiming
(*用Map對(duì)list中的每個(gè)數(shù)判斷是否偶數(shù),然后用Count數(shù)出結(jié)果中True的個(gè)數(shù)*)
Select[list, EvenQ] // Length // AbsoluteTiming
(*選取list中的所有偶數(shù),然后求結(jié)果列表長(zhǎng)度*)
Count[EvenQ@list, True] // AbsoluteTiming
(*利用EvenQ的Listable屬性直接判斷l(xiāng)ist的每個(gè)數(shù)是否偶數(shù),然后數(shù)出結(jié)果中True的個(gè)數(shù)*)
Sum[Boole[EvenQ@i], {i, list}]
(*對(duì)list中的每個(gè)元素判斷是否偶數(shù),將結(jié)果相加*)
結(jié)果如下圖
這個(gè)遍歷的例子舉得不算特別恰當(dāng),但也能說(shuō)明一些問(wèn)題了:Mathematica中內(nèi)置了許多神奇的函數(shù),其中大部分只要使用得當(dāng)效率都比循環(huán)高(而且不是一點(diǎn)半點(diǎn))。就算非要用循環(huán),也要記得(任何能用Do代替For的時(shí)候)
這個(gè)遍歷的例子舉得不算特別恰當(dāng),但也能說(shuō)明一些問(wèn)題了:Mathematica中內(nèi)置了許多神奇的函數(shù),其中大部分只要使用得當(dāng)效率都比循環(huán)高(而且不是一點(diǎn)半點(diǎn))。就算非要用循環(huán),也要記得(任何能用Do代替For的時(shí)候)
Do比For快
,(遍歷列表時(shí))
Scan比Do快
用向量(矩陣)運(yùn)算代替循環(huán)
這個(gè)例子來(lái)自如何用 Python 科學(xué)計(jì)算中的矩陣替代循環(huán)? - Kaiser 的回答,我只是把代碼從Python翻譯成了Mathematica而已。選這個(gè)例子是因?yàn)樗斜容^明確的物理意義,而且效率對(duì)比非常明顯
代碼如下
AbsoluteTiming[
n = 100;
u = unew = SparseArray[{{1, _} - 1}, {n, n}] // N // Normal;
For[k = 1, k = 3000, k++,
For[i = 2, i n, i++,
For[j = 2, j n, j++,
unew[[i, j]] =
0.25 (u[[i + 1, j]] + u[[i - 1, j]] + u[[i, j + 1]] +
u[[i, j - 1]])
]
];
u = unew;
];
u1 = u;
]
(*用三重循環(huán),迭代3000次*)
ArrayPlot[u1, DataReversed - True, ColorFunction - "TemperatureMap"]
(*用ArrayPlot繪圖*)
AbsoluteTiming[
n = 100;
u = SparseArray[{{1, _} - 1}, {n, n}] // N // Normal;
Do[
u[[2 ;; -2, 2 ;; -2]] =
0.25 (u[[3 ;; -1, 2 ;; -2]] + u[[1 ;; -3, 2 ;; -2]] +
u[[2 ;; -2, 3 ;; -1]] + u[[2 ;; -2, 1 ;; -3]]),
{k, 1, 3000}];
u2 = u;
]
(*用矩陣運(yùn)算,迭代3000次*)
ArrayPlot[u2, DataReversed - True, ColorFunction - "TemperatureMap"]
(*用ArrayPlot繪圖*)
運(yùn)行結(jié)果For循環(huán)用時(shí)136秒,矩陣運(yùn)算用時(shí)不足0.5秒,且兩者答案完全一樣。在算法完全相同的情況下兩種寫(xiě)法有著超過(guò)200倍的效率差距
(圖片太長(zhǎng)了這里就不直接顯示了,鏈接放在下面)
===========================我是結(jié)尾的分隔線(xiàn)===============================
這個(gè)答案其實(shí)從一開(kāi)始就跑題了,還寫(xiě)了這么長(zhǎng)的目的就在于希望讓大家切實(shí)地感受到循環(huán)的低效并安利一下Mathematica中其它高效的方法。正如wolray的答案中說(shuō)的,既然選擇了使用Mathematica就應(yīng)該多利用些MMA獨(dú)有的美妙函數(shù),畢竟如果只是用循環(huán)的話(huà)C和Fortran之類(lèi)的語(yǔ)言效率比MMA不知高到哪里去了。。。。。。
當(dāng)
然我也不是讓大家就不用循環(huán)了,畢竟很多時(shí)候循環(huán)的直觀性和易讀性帶來(lái)的便利遠(yuǎn)遠(yuǎn)比那點(diǎn)效率重要。只是希望大家在循環(huán)之前能稍稍想一下,自己的目的是不是
一定要用循環(huán)?可不可以用內(nèi)置函數(shù)代替循環(huán)?就像上面的幾個(gè)例子,將循環(huán)換成內(nèi)置函數(shù)程序的簡(jiǎn)潔性和效率都大幅提高,長(zhǎng)此以往相信你一定會(huì)愛(ài)上MMA的~
題外話(huà)——關(guān)于用編譯提速循環(huán)
在
MMA中如果一定要使用循環(huán)又對(duì)效率有一定要求的話(huà),可以選擇使用編譯,效率能有極大的提高。比如上面的第4個(gè)例子使用Complie編譯過(guò)后的Do循環(huán)
用時(shí)只有1.86秒,速度提升了將近100倍。如果電腦中有C編譯器的話(huà)還可以在Compile中加入CompilationTarget -
"C"選項(xiàng),速度還能有所提升。編譯過(guò)后的代碼如下:
In[10]:= cf = Compile[{{n, _Integer}, {times, _Integer}},
Module[{u},
u = ConstantArray[0., {n, n}];
u[[1]] = ConstantArray[1., n];
Do[
Do[u[[i, j]] =
0.25 (u[[i + 1, j]] + u[[i - 1, j]] + u[[i, j + 1]] +
u[[i, j - 1]]),
{i, 2, n - 1}, {j, 2, n - 1}
], {k, 1, times}];
u
]
];
u3 = cf[100, 3000]; // AbsoluteTiming
ArrayPlot[u3, DataReversed - True, ColorFunction - "TemperatureMap"]
Out[11]= {1.86055, Null}
前3個(gè)例子也都可以通過(guò)編譯提速很多,這里就不放代碼了,有興趣的同學(xué)可以自己動(dòng)手試一試,如果遇到問(wèn)題歡迎在評(píng)論中與我交流。
需要注意的是編譯有很多注意事項(xiàng),這里推薦一篇寫(xiě)的很好的教程,編譯中常見(jiàn)的問(wèn)題里面都有很好的講解:怎樣編譯(Compile)/編譯的通用規(guī)則/學(xué)會(huì)這6條,你也會(huì)編譯
但是一般來(lái)講編譯很麻煩,而且再怎么編譯效率也很難趕上直接用C,所以個(gè)人并不特別建議MMA初學(xué)者學(xué)習(xí)編譯。
編寫(xiě)循環(huán)的技巧,for循環(huán)包含了常見(jiàn)的計(jì)數(shù)器式循環(huán)。由于通常for循環(huán)比while循環(huán)更容易寫(xiě),也執(zhí)行得更快,所以for循環(huán)一般是你遍歷序列或其他可迭代對(duì)象時(shí)的首選。事實(shí)上,作為一條通用法則,你應(yīng)該克制在Python中使用計(jì)數(shù)方式的誘惑——Python提供的迭代工具,能幫你把像C這樣低級(jí)語(yǔ)言中循環(huán)集合體的工作自動(dòng)化。不過(guò),有些情況下你還是需要以更為特定的方式進(jìn)行迭代。例如,如果你需要在列表中每隔一個(gè)元素或每隔兩個(gè)元素進(jìn)行訪問(wèn),或是要同時(shí)修改列表呢?如果在同一個(gè)for循環(huán)內(nèi),并行遍歷一個(gè)以上的序列呢?如果你也需要進(jìn)行索引呢?
你總是可以用while循環(huán)和手動(dòng)索引運(yùn)算來(lái)編寫(xiě)這些獨(dú)特的循環(huán),但是python提供了一套內(nèi)置函數(shù),可以幫你在for循環(huán)內(nèi)定制迭代:·內(nèi)置函數(shù)range (Python 0.X及之后版本可用)返回一系列連續(xù)增加的整數(shù),可作為for中的索引。內(nèi)置函數(shù)zip (Python 2.0及之后版本可用)返回一系列并行元素的元組,可用于在for中內(nèi)遍歷多個(gè)序列。內(nèi)置函數(shù)enumerate (Python 2.3及之后版本可用)同時(shí)生成可迭代對(duì)象中元素的值和索引,因而我們不必再手動(dòng)計(jì)數(shù)。內(nèi)置函數(shù)map (Python 1.0及之后版本可用)在Python 2.X中與zip有著相似的效果,但是在3.X中 map 的這一角色被移除了。因?yàn)閒or循環(huán)可能會(huì)比基于while的計(jì)數(shù)器循環(huán)運(yùn)行得更快,所以借助這些工具并盡可能地使用for循環(huán),會(huì)讓你受益匪淺。讓我們?cè)诔R?jiàn)的使用場(chǎng)景下,依次看一看這些內(nèi)置函數(shù)吧。我們將會(huì)看到,它們的用法在Python 2.X和3.X中稍有不同,同時(shí)它們中的一些要比其他的更加有效。
for(a;b;c) a為初始化條件,b為判斷條件,成立就進(jìn)行for里面的,不成立直接退出。 c為條件變化。abc三個(gè)語(yǔ)句之間為分號(hào),可以為空語(yǔ)句,即不加任何信息。 b為判斷是否進(jìn)行的依據(jù),一般不要省,要不然會(huì)永遠(yuǎn)循環(huán)下去。 舉個(gè)例子: for(;;)永遠(yuǎn)運(yùn)行 for(;a100;){a++;}a長(zhǎng)到100的時(shí)候就會(huì)退出for循環(huán)了,只有中間加了,前面后面得語(yǔ)句隨意,但目的是要for有開(kāi)始也有結(jié)束,如下面兩種情況 for(int i = 0; i100; i++)循環(huán)100次 int i=0; for(;i100;){i++;}也是循環(huán)100次