假币问题
原文章:https://blog.csdn.net/qq_39630587/article/details/79243348
假幣問題
在八枚外觀相同的硬幣中,有一枚是假幣,并且已知假幣與真幣的重量不同,但不知道假幣與真幣相比較輕還是較重。可以通過一架天平來任意比較兩組硬幣,設計一個高效的算法來檢測出這枚假幣。
我們先假設一個條件:已知假幣比真幣輕
二分查找算法實現:時間復雜度(O(log(以2為底n的對數)))
思路:把n枚硬幣分成兩堆,每堆有枚硬幣,如果n為奇數的話,就留下一枚額外的硬幣,然后把兩堆硬幣放在天平上。如果兩堆硬幣重量相同,那么放在旁邊的硬幣就是假幣;否則我們可以用同樣的方式對較輕的一堆硬幣進行處理,這堆硬幣中一定包含那枚假幣。注意,即使我們把硬幣分成了兩個子集,但在每次稱重之后,我們只需要解決一個規模為原來一半的問題。所以這是一個減治算法而不是一個分治算法。
public class Main {static int[] a = {2, 2, 2, 2, 2, 2, 1, 2};public static void main(String[] args) {int l = 0;int r = a.length-1;System.out.println(f2(l, r));}private static int f2(int l, int r) {int n = r - l + 1;int mid = (l+r) / 2;if(n == 1) {return l;}/*** n為總個數,mid為中值* n為偶數,則將n枚硬幣分成兩堆數量相等的硬幣,對輕的一堆迭代稱重* n為奇數,留下一枚硬幣,對n-1枚硬幣按偶數繼續操作* */if (n % 2 == 0) {if (sum(l, mid) == sum(mid+1, r)) {return l - 1;} else if (sum(l, mid) < sum(mid+1, r)){return f2(l, mid);} else {return f2(mid+1, r);}} else {return f2(l+1, r);}}/*** 獲取指定區域內硬幣的重量* */private static int sum(int l, int r) {int sum = 0;for (int i = l; i <= r; i++) {sum += a[i];}return sum;} }三分查找算法實現:時間復雜度(O(log(以3為底n的對數)))
思路:將n枚硬幣分成三組,前兩組有組硬幣,其余的硬幣作為第三組,將前兩組硬幣放到天平上,如果它們的重量相同,則假幣一定在第三組中,用同樣的方法對第三組進行處理;如果前兩組的重量不同,則假幣一定在較輕的那一組中,用同樣的方法對較輕的那組硬幣進行處理。這也是一個減治算法。
二分查找算法適用于單調的一個函數,即數組序列要么升序,要么降序
而三分查找算法使用于凸函數,常用來求極值問題。
在假幣問題中,三分查找在n較大的情況下,效率是優于二分查找的。
最復雜的情況
下面來回到最開始的問題,在不知道假幣輕重的情況下,我們通過下面的算法來實現,其時間復雜度為O(log(以2為底n的對數))
相比來說,這種情況思考起來比較復雜,但是好在邏輯清楚,只要理解了思路,算法還是好實現的,下面我們來舉一個例子,假設有八枚硬幣,其中有一枚硬幣是假幣。但是我們不知道假幣是比真幣重還是輕。先把八枚硬幣編號,分別表示為a,b,c,d,e,f,g,h,從八枚硬幣中任取六枚a,b,c,d,e,f,在天平兩端各放三枚進行比較。假設a,b,c三枚放在天平的一端,d,e,f三枚放在天平的另一端,可能出現三種比較結果:
⑴ a+b+c>d+e+f
⑵ a+b+c=d+e+f
⑶ a+b+c<d+e+f
若a+b+c>d+e+f,可以肯定這六枚硬幣中必有一枚為假幣,同時也說明g、h為真幣。這時可將天平兩端各去掉一枚硬幣,假設去掉c、f,同時將天平兩端的硬幣各換一枚,假設硬幣b、e作了互換,然后進行第二次比較,比較的結果同樣可能有三種:
① a+e>d+b:這種情況表明天平兩端去掉硬幣c、f且硬幣b、e互換后,天平兩端的輕重關系保持不變,從而說明了假幣必然是a,d中的一個,這時我們只要用一枚真幣(例如h)和a進行比較,就能找出假幣。若a>h,則a是較重的假幣;若a=h,則d為較輕的假幣;不可能出現a<h的情況。(為什么?很簡單,因為我們判斷了a,d中有一個假幣那么e,b都是真幣,則e=d。而a+e>d+b可以推出a>d,所以不管a是真是假都不可能出現a<h情況出現)
② a+e=d+b:此時天平兩端由不平衡變為平衡,表明假幣一定在去掉的兩枚硬幣c,f中,同樣用一枚真幣(例如h)和c進行比較,若c>h,則c是較重的假幣;若c=h,則f為較輕的假幣;不可能出現c<h的情況。
③ a+e<d+b:此時表明由于兩枚硬幣b,e的對換,引起了兩端輕重關系的改變,那么可以肯定b或e中有一枚是假幣,同樣用一枚真幣(例如h)和b進行比較,若b>h,則b是較重的假幣;若b=h,則e為較輕的假幣;不可能出現b<h的情況。
public class Main {static int[] a = {2, 2, 2, 2, 2, 2, 1, 2};static int flag = 0;public static void main(String[] args) {int p;if (sum(0, 2) == sum(3, 5)) {p = fp(6, 7);} else if (sum(0, 2) > sum(3, 5)){if (a[0] + a[4] > a[3] + a[1]) {p = fp(0, 3);} else if (a[0] + a[4] == a[3] + a[1]) {p = fp(2, 5);} else {p = fp(1, 4);}} else {if (a[0] + a[4] > a[3] + a[1]) {p = fp(1, 4);} else if (a[0] + a[4] == a[3] + a[1]) {p = fp(2, 5);} else {p = fp(0, 3);}}if (flag == 1) {System.out.println("假幣輕,為第" + p + "枚");} else {System.out.println("假幣重,為第" + p + "枚");}}private static int fp(int l, int r) {int H, L;int x = (l+1) % 8;if(a[l] > a[r]) {H = l;L = r;} else {H = r;L = l;}if (a[H] > a[x]) {flag = 1;return H;} else {flag = -1;return L;}}/*** 獲取指定區域內硬幣的重量* */private static int sum(int l, int r) {int sum = 0;for (int i = l; i <= r; i++) {sum += a[i];}return sum;} }總結
- 上一篇: 浅析WLAN——无线局域网
- 下一篇: ssh “Missing privile