猜想: 對於任意大於1的自然數n,若n為奇數,則將n變為3n+1,否則變為n的一半。
經過若干次這樣的變換,一定會使n變為1。例如3->10->5->16->8->2->1。
輸入n,輸出變換的次數。n≤10^9。
樣例輸入:3
樣例輸出:7
不假思索的寫出下面的代碼:
#include<stdio.h> int main(void) { int n; int count=0; scanf("%d", &n); while(n > 1) { if(n%2 != 0) n = 3*n + 1; else n /= 2; count++; } printf("%d\n", count); return 0; }
然而,程序正確嗎?很不幸,如果輸入987654321,答案為1,這顯然是錯誤的。通過調試或者在循環體中用printf語句打印出n的值,看到n的值為負數,導致一次循環后程序退出。從這里可以獲悉n的值溢出了,因為整型最大值為2^31-1 = 2147483647,大約為21億,而由題意n的最大值為10億(10^9),所以在n的值頗大且為奇數時乘以3是危險的,會導致溢出。
解決方案如下:
因為奇數*奇數=奇數,所以經過n=3*n+1的計算后,n的值必然是偶數,並且下次循環必然做運算n/=2,所以這里可以合並這兩步,也就是n為奇數的情況下做運算n=floor(1.5*n+0.5),由於double值的誤差問題,我們可以用n=floor(1.5*n+1)(floor函數接收double類型的參數,返回不大於給定參數的最大整形數,返回值類型為double),將取整后的double值賦給整形從而丟棄小數點。
修改后的程序如下:
#include<stdio.h> #include<math.h> int main(void) { int n; int count=0; scanf("%d", &n); while(n > 1) { if(n%2 != 0) { n = floor(1.5*n + 1); count += 2; } else { n = n / 2; count++; } } printf("%d\n", count); return 0; }
再次用987654321測試,得到結果180次。再者,如果對n的限制為整形int的范圍,該程序就不能完成要求了,那么我們可以使用long long int。
參考資料:《算法競賽入門經典》——劉汝佳
最后一些疑惑(望看到的高手能解答一二,吾將不勝感激):
1、這里第二個程序是我依照作者的提示寫的,自認應該正確吧:-),但是還是有一些疑惑,比如我們考慮了第一次n的輸入值,但是循環
中的第二次,第三次...呢?我們如何說明以后循環的值不會發生溢出呢?
比如開始時n的值為7,那么一次循環后其值為11,而11>7。也就是說二次循環的值大於7。
2、再者,我們如何知道經過若干次的變換后一定會得到1呢,也就是說我們如何知道函數收斂呢?嗯...這貌似是一個數學問題啊。
All Rights Reserved.
Author:海峰:)
Copyright © xp_jiang.
轉載請標明出處:http://www.cnblogs.com/xpjiang/p/4129340.html
———————————————————^_^我是更新線2014年12月5日 16:15:21^_^——————————————————
關於這個問題的答案我是在數學科普神犇顧森的博客中找到的,其博客地址在這里Matrix67(順便推薦,很好的博客呢),原文見這里千萬別學數學:最折磨人的數學未解之謎(一),下面把與該問題相關的文字摘錄在這里:
數學之美不但體現在漂亮的結論和精妙的證明上,那些尚未解決的數學問題也有讓人神魂顛倒的魅力。和 Goldbach 猜想、 Riemann 假設不同,有些懸而未解的問題趣味性很強,“數學性”非常弱,乍看上去並沒有觸及深刻的數學理論,似乎是一道可以被瞬間秒殺的數學趣題,讓數學愛好者們“不找到一個巧解就不爽”;但令人稱奇的是,它們的困難程度卻不亞於那些著名的數學猜想,這或許比各個領域中艱深的數學難題更折磨人吧。
作為一本數學趣題集, Mathematical Puzzles 一書中竟把仍未解決的數學趣題單獨列為一章,可見這些問題有多么令人着迷。我從這一章里挑選了一些問題,在這里和大家分享一下。這本書是 04 年出版的,書里提到的一些“最新進展”其實已經不是最新的了;不過我也沒有仔細考察每個問題當前的進展,因此本文的信息並不保證是 100% 准確的,在此向讀者們表示歉意。
3x + 1 問題
從任意一個正整數開始,重復對其進行下面的操作:如果這個數是偶數,把它除以 2 ;如果這個數是奇數,則把它擴大到原來的 3 倍后再加 1 。序列是否最終總會變成 4, 2, 1, 4, 2, 1, … 的循環?
這個問題可以說是一個“坑”——乍看之下,問題非常簡單,突破口很多,於是數學家們紛紛往里面跳;殊不知進去容易出去難,不少數學家到死都沒把這個問題搞出來。已經中招的數學家不計其數,這可以從 3x + 1 問題的各種別名看出來: 3x + 1 問題又叫 Collatz 猜想、 Syracuse 問題、 Kakutani 問題、 Hasse 算法、 Ulam 問題等等。后來,由於命名爭議太大,干脆讓誰都不沾光,直接叫做 3x + 1 問題算了。
3x + 1 問題不是一般的困難。這里舉一個例子來說明數列收斂有多么沒規律。從 26 開始算起, 10 步就掉入了“421 陷阱”:
26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, …
但是,從 27 開始算起,數字會一路飆升到幾千多,你很可能會一度認為它脫離了“421 陷阱”;但是,經過上百步運算后,它還是跌了回來:
27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, …
額外的,再說一點:如上所述,既然數列的收斂如此沒有規律,那么上面我們的程序可能是錯誤的(包括優化過后的那個)。舉一個例子,我們用 long long int 做一個測試:
#include<math.h>
int main(void) { long long int n; int count=0; // 統計計算次數
scanf("%I64d", &n); // long long int在windows下一定要用%I64d讀入數據,否則會出問題(比如數據截斷什么的)
long long max = 0; // 記錄計算過程中出現的最大值
while(n > 1) { if(n%2 != 0) { //n = floor(1.5*n + 1);
n = n * 3 + 1; } else { n = n / 2; } count++; printf("%I64d\n", n); // 輸出也使用%I64d
if(n > max) max = n; } printf("%d\n%I64d\n", count, max); return 0; }
我們輸入704511,最后那個printf函數的輸出結果:242,56991483520。可見其中間值有5百億這么大,而用 n = floor(1.5*n + 1) 語句代替 n = n * 3 + 1;輸出的中間值為28495741760,也有2百億那么大,int類型存不下這么大的數據,所以更新線之前的兩個程序在測試強度不夠的情況下表面上看是正確,實則是錯誤的。
最后,寫的這里,應該可以告一段落了。