posted @ 2006-09-11 21:02 chenger| 編輯 收藏
class A
{
public:
??? template <typename T>
??? T f(T val);
??? template <>
??? int f<int>(int val);
};
我用的是g++ 3.4.2 mingw版本。編譯上面這段代碼時(shí),錯(cuò)誤信息如下:
5: error: invalid explicit specialization before '>' token
5: error: explicit specialization in non-namespace scope `class A'
如果把f的定義放到全局作用域中,就不會(huì)出錯(cuò)。而上面這段代碼在VC++ 8.0下可以編譯通過。運(yùn)行起來也沒有問題。別的編譯器我沒有試過。
Update:多謝周星星的指點(diǎn),比較“常規(guī)”的寫法如下:
class A
{
public:
??? template <typename T>
??? T f(T val);
};
template <typename T>
T A::f(T val)
{
??? // ...
}
template <>
int A::f<int>(int val)
{
??? //...
}
這種寫法就沒有任何問題(在g++ 3.4.2和VC++ 8.0下均表現(xiàn)正常)。至于為什么前面的寫法導(dǎo)致g++下報(bào)錯(cuò),還不是很清楚。
posted @ 2006-10-12 16:28 chenger 閱讀(3195) | 評(píng)論 (4) | 編輯 收藏
言歸正傳。Ruby中的名字規(guī)則主要是根據(jù)名字的第一個(gè)字母來決定這個(gè)名字的使用方式。具體來說,
- 局部變量,方法名,方法參數(shù):以小寫字母或下劃線開頭,以'_'連接。
Example:i,note_controller - 常量:全部大寫,以'_'連接
Example:A_NUM - 類,模塊(module):都是開頭大寫(因?yàn)轭惷侨肿兞浚?,其他小寫并且直接連接在一起
Example:ActiveRecord - 全局變量:以'$'開頭(肯定是跟Perl學(xué)的,我覺得不怎么好)
- 實(shí)例變量(instance variable):以'@'開頭(同上)
- 類變量(class variable):以'@@'開頭(詭異)
posted @ 2006-09-29 18:56 chenger 閱讀(425) | 評(píng)論 (0) | 編輯 收藏
Ruby的源代碼還充分體現(xiàn)了拿來主義的精神,能重用的決不自己寫:比如Hash表就用了一個(gè)通用的Hash表實(shí)現(xiàn),正則表達(dá)式則使用了GNU的regex庫,random是有名的MT19937(也是日本人寫的)。嘗試了一下編譯,在mingw上執(zhí)行標(biāo)準(zhǔn)三部曲:./configure,make,make install,一切OK。
posted @ 2006-09-24 14:50 chenger 閱讀(413) | 評(píng)論 (0) | 編輯 收藏
size_t little(size_t i)
{
??? size_t ones = calc_ones(i);
??? if(ones == i)
??????? cout << i << "\n";
??? if(i < ones)
??????? if((ones - i)/9 > 1)
??????????? return i - (ones - i)/8;
??? if(i > ones)
??????? return ones;
??? return i - 1;
}
這是C++版本。主循環(huán)也要略微改變一下:
void solve()
{
??? size_t max = 10000000000;
??? for(size_t i = max;i > 0;i = little(i));
}
可以看到,現(xiàn)在循環(huán)從大到小。little函數(shù)找到下一個(gè)可能滿足題目約束的i。在little函數(shù)中,首先計(jì)算小于i的1的個(gè)數(shù)ones,如果ones和i相等,就將i輸出(這就是題目要求干的事)。如果i小于ones,那么就要在小于i的自然數(shù)中找下一個(gè)可能滿足條件的數(shù)。因?yàn)樗阉鞯姆秶怀^10^10,所以一個(gè)數(shù)中至多含有9個(gè)1,按照這種極端情況,也必須將i減少(ones-i)/8才有可能滿足條件(這里之所以是8,因?yàn)橥瑫r(shí)i也減少了)。如果i大于ones,考慮一個(gè)小于i的數(shù)i',可以考慮一下calc_ones(i')的取值,極端情況,[i',i)的范圍內(nèi)的整數(shù)沒有一個(gè)包含1,也就是說當(dāng)i減少到i'時(shí)1的個(gè)數(shù)沒有損失,那么calc_ones(i') = calc_ones(i),如果i'>calc_ones(i),則就有i'>calc_ones(i'),直到i'=calc_ones(i),因此下一個(gè)需要查看的數(shù)就是calc_ones(i)。其實(shí)上面這一段討論可以用一個(gè)式子來概括:對(duì)i'<i,calc_ones(i)-9*(i-i') <= calc_ones(i') <= calc_ones(i)。這樣就能大大提高速度了。
posted @ 2006-09-16 15:35 chenger 閱讀(887) | 評(píng)論 (4) | 編輯 收藏
posted @ 2006-09-13 23:13 chenger 閱讀(975) | 評(píng)論 (0) | 編輯 收藏
posted @ 2006-09-11 19:01 chenger 閱讀(722) | 評(píng)論 (3) | 編輯 收藏
我的解法:
#include <iostream>
#include <limits>
#include <cstddef>
using namespace std;
size_t calc_ones(size_t n)
{
??? const size_t save = n;
??? size_t sum = 0,ten = 1,cnt = 1;
??? if(n%10 > 1)
??????? sum = 1;
??? n /= 10;
??? for(;n;n /= 10)
??? {
??????? size_t r = n%10;
??????? if(r == 1)
??????????? sum += save + (r*cnt - 10*n)*ten;
??????? else if(r != 0)
??????????? sum += (10 + r*cnt)*ten;
??????? ten *= 10;
??????? ++cnt;
??? }
??? return sum;
}
void solve()
{
??? size_t max = numeric_limits<size_t>::max();
??? for(size_t i = 1;i < max;++i)
??????? if(calc_ones(i) == i)
??????????? cout << i << "\n";
}
int main()
{
??? solve();
??? return 0;
}
在VS2005下編譯運(yùn)行,輸出結(jié)果為:
199992 199993 199994 199995 199996 199997 199998 199999 200000 1599992 1599993 1599994 1599995 1599996 1599997 1599998 1599999 1600000 1600001 2600000 13200000 13200001 35000000 35199992 35199993 35199994 35199995 35199996 35199997 35199998 35199999 35200000 117463827 500000000 500199992 500199993 500199994 500199995 500199996 500199997 500199998 500199999 500200000 501599992 501599993 501599994 501599995 501599996 501599997 501599998 501599999 501600000 501600001 502600000 513200000 513200001 535000000 535199992 535199993 535199994 535199995 535199996 535199997 535199998 535199999 535200000
可以證明,再往上就沒有了。跑得比較慢,需要好幾分鐘。我考慮過進(jìn)一步縮小檢索的范圍,應(yīng)該是可以做到的,不過沒有實(shí)現(xiàn)。
Update:上面的算法有很大的改進(jìn)余地,主要來自張沈鵬同學(xué)給出的程序,我專門寫了一篇文章來討論,這里?;蛘呖梢灾苯涌?a href="/zuroc/archive/2006/09/16/12540.html">張沈鵬同學(xué)的文章。
posted @ 2006-09-08 13:05 chenger 閱讀(1760) | 評(píng)論 (13) | 編輯 收藏
Turbo下載
Update:說一下下載文件的情況。有兩個(gè)部分,一個(gè)是prerequisites,另一個(gè)是main installation。奇怪的是prerequisites當(dāng)中還包括.NET Framework 1.1,是不是太old了一點(diǎn)?這部分prerequisites和Borland Develop Studio基本上是一樣的。
繼續(xù)Update:很不幸,未能成功。安裝了一遍之后,啟動(dòng)時(shí)接連保錯(cuò),似乎是在讀取rtl100.bpl和coreide100.bpl的時(shí)候出了段錯(cuò)誤,結(jié)果IDE是啟動(dòng)了,和C++有關(guān)的項(xiàng)目還有組件一個(gè)都沒有……雖然還剩了諸如編譯器和編輯器調(diào)試器等內(nèi)容,但意義不大??紤]到機(jī)子上原來還裝了個(gè)C++ Builder 6,卸之,再重裝一遍Turbo C++,還是老樣子……徹底放棄。
posted @ 2006-09-06 07:32 chenger 閱讀(844) | 評(píng)論 (7) | 編輯 收藏
還是直接給出代碼:
#include <iostream>
#include <string>
using namespace std;
int main()
{
??? const char *p = string("hello").c_str();
??? cout << p << endl;
??? return 0;
}
想想輸出結(jié)果是什么?
這時(shí)VS2005和g++的結(jié)果就不一樣了。VS2005上什么都不輸出,而g++ 3.4上則輸出了似乎非常合理的結(jié)果:hello,符合很多人的預(yù)期。不過查了標(biāo)準(zhǔn)以后,還是把票投給VS2005。
首先,string("hello")產(chǎn)生了一個(gè)temporary object,或者說臨時(shí)對(duì)象。C++標(biāo)準(zhǔn)對(duì)臨時(shí)對(duì)象的生存期(life time)有明確的規(guī)定,可見標(biāo)準(zhǔn)12.2節(jié)第3-5條。第3條討論了臨時(shí)對(duì)象的析構(gòu)時(shí)間:
3. ... Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception.
這又涉及到full-expression的定義了,參見1.9節(jié)。整個(gè)對(duì)p的初始化構(gòu)成了一個(gè)full-expression。在下結(jié)論之前,還要先看看第4、5條,分別討論了兩個(gè)例外情形,一個(gè)是將臨時(shí)對(duì)象作為初始化子,例如string s = string("hello");第二是將一個(gè)引用變量綁定到這個(gè)臨時(shí)對(duì)象上,例如const string &s = string("hello"),總而言之,在這兩種情形中可以通過一個(gè)名字來存取這個(gè)對(duì)象,此對(duì)象的生存期就延長到變量名的作用域結(jié)束。除此之外,都按照第3條處理。
有了這些準(zhǔn)備,拿前面給的例子往里套就明白了:這里沒有出現(xiàn)4、5所指出的例外,因此第3條的原則適用。而不管full-expression如何,可以確定的是在p被初始化之后臨時(shí)對(duì)象string("hello")的析構(gòu)函數(shù)就應(yīng)該被調(diào)用。在VS2005中進(jìn)行調(diào)試,可以發(fā)現(xiàn)string析構(gòu)函數(shù)調(diào)用的時(shí)間就在p被初始化之后,語句cout << p << endl執(zhí)行之前。手頭沒有方便的工具來調(diào)試g++編譯出來的程序(不太會(huì)用gdb調(diào)試C++程序,特別涉及到STL)。至于之后p指向的內(nèi)存到底如何,則和具體的string實(shí)現(xiàn)相關(guān)了。這樣分析下來,VS2005的結(jié)果還是比較不錯(cuò)的,而g++的結(jié)果則容易讓人產(chǎn)生誤解。
Update:察看g++編譯出來的匯編代碼,發(fā)現(xiàn)g++同樣在表達(dá)式求值后析構(gòu)了臨時(shí)對(duì)象,只不過由于實(shí)現(xiàn)上的原因,p指向的內(nèi)容還沒有清空。
posted @ 2006-09-04 23:23 chenger 閱讀(1367) | 評(píng)論 (13) | 編輯 收藏
?
def sievePerformance(n)
??? r = Benchmark.realtime() do
??????? sieve = Array.new(n,true)
??????? sieve[0..1] = [false,false]
??????? 2.upto(n) do |i|
??????????? if sieve[i]
??????????????? (2*i).step(n,i) do |j|
??????????????????? sieve[j] = false
??????????????? end
??????????? end
??????? end
??? end
??? r
end
這段代碼抄自前面Robert C.Martin先生的blog,對(duì)篩法作性能測(cè)試。初看起來,程序的主體是二重循環(huán),因此算法的復(fù)雜性好像是O(N2)之類的玩意?要么是O(NlnN)?
?
下圖是Ruby自帶的benchmark模塊測(cè)量的結(jié)果,上限N從10000到500000,步長20000。Rober C.Martin的文章里也有一張圖,是從1000000到5000000,從圖中可以看到,他電腦的性能遠(yuǎn)勝于我,我要是從1000000到5000000這么跑一遍,花兒都謝了……總之,實(shí)測(cè)的結(jié)果是:這個(gè)算法的性能基本上是線性的。出于對(duì)ruby這樣的解釋型語言的某種不信任,我又把這段程序用C++重寫了一遍,拿C標(biāo)準(zhǔn)庫提供的clock函數(shù)測(cè)量時(shí)間,結(jié)果在N小于10000000的時(shí)候,基本上呈線性,但再往后花費(fèi)的時(shí)間就開始超過線性增長了。
下面我給一個(gè)比較粗略的分析,解釋為什么這個(gè)算法的復(fù)雜度表現(xiàn)為線性。首先,我認(rèn)為主要花費(fèi)時(shí)間的是對(duì)sieve數(shù)組的讀寫,循環(huán)變量的增加應(yīng)該可以忽略。如果p<N是素?cái)?shù),那么就要進(jìn)入內(nèi)循環(huán)將i的倍數(shù)“挖掉”,也就是對(duì)sieve的相應(yīng)元素賦值,要進(jìn)行[N/p]-1次。這樣就得到總共的賦值次數(shù)S為:

其中p為素?cái)?shù)。顯然

數(shù)論中有個(gè)Mertens定理可以估計(jì)上面括號(hào)中的和式,結(jié)果為

其中c是一個(gè)常數(shù)??梢钥吹?,在N很大時(shí)和式的主要部分為NlnlnN。而lnlnN是一個(gè)增長極慢的函數(shù),lnln105=2.44,lnln109=2.91,幾乎就可以當(dāng)常數(shù)處理(至少在32位無符號(hào)整數(shù)范圍內(nèi))。其他的一些項(xiàng),比如循環(huán)變量的步進(jìn),都是O(N),這也就不難理解整個(gè)程序的性能是幾乎是O(N)了。

Update:上面的代碼有個(gè)很明顯的問題,就是內(nèi)循環(huán)應(yīng)該從i*i開始,而不是2*i,這樣對(duì)于比較大的N,性能提高很明顯(接近一半)。另外一個(gè)可改進(jìn)的地方是外層循環(huán)的upto(n),可以改為upto(Integer(Math.sqrt(n)),其實(shí)這兩個(gè)改動(dòng)效果是重疊的,任意改一個(gè)就差不多了。賦值次數(shù)S應(yīng)為:

結(jié)果為:

可以看到效率的提升是很明顯的,畢竟lnln232也才不到3.1,ln2約為0.7。
posted @ 2006-09-02 21:16 chenger 閱讀(686) | 評(píng)論 (0) | 編輯 收藏