减治法解决八枚硬币问题/假币问题(JAVA)----二分,三分,不知轻重的情况
八枚硬幣問(wèn)題
在八枚外觀相同的硬幣中,有一枚是假幣,并且已知假幣與真幣的重量不同,但不知道假幣與真幣相比較輕還是較重。可以通過(guò)一架天平來(lái)任意比較兩組硬幣,設(shè)計(jì)一個(gè)高效的算法來(lái)檢測(cè)出這枚假幣。
我們先假設(shè)一個(gè)條件:已知假幣比真幣輕
二分查找算法實(shí)現(xiàn):時(shí)間復(fù)雜度(O(log(以2為底n的對(duì)數(shù))))
思路:把n枚硬幣分成兩堆,每堆有枚硬幣,如果n為奇數(shù)的話,就留下一枚額外的硬幣,然后把兩堆硬幣放在天平上。如果兩堆硬幣重量相同,那么放在旁邊的硬幣就是假幣;否則我們可以用同樣的方式對(duì)較輕的一堆硬幣進(jìn)行處理,這堆硬幣中一定包含那枚假幣。注意,即使我們把硬幣分成了兩個(gè)子集,但在每次稱重之后,我們只需要解決一個(gè)規(guī)模為原來(lái)一半的問(wèn)題。所以這是一個(gè)減治算法而不是一個(gè)分治算法。
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為總個(gè)數(shù),mid為中值* n為偶數(shù),則將n枚硬幣分成兩堆數(shù)量相等的硬幣,對(duì)輕的一堆迭代稱重* n為奇數(shù),留下一枚硬幣,對(duì)n-1枚硬幣按偶數(shù)繼續(xù)操作* */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);}}/*** 獲取指定區(qū)域內(nèi)硬幣的重量* */private static int sum(int l, int r) {int sum = 0;for (int i = l; i <= r; i++) {sum += a[i];}return sum;} }
思路:將n枚硬幣分成三組,前兩組有組硬幣,其余的硬幣作為第三組,將前兩組硬幣放到天平上,如果它們的重量相同,則假幣一定在第三組中,用同樣的方法對(duì)第三組進(jìn)行處理;如果前兩組的重量不同,則假幣一定在較輕的那一組中,用同樣的方法對(duì)較輕的那組硬幣進(jìn)行處理。這也是一個(gè)減治算法。
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(f3(l, r));}private static int f3(int l, int r) {int n = r - l + 1;int x;if(n == 1) {return l;}/*** n為總個(gè)數(shù)* 將n分成n/3, n/3, n-2(n/3)三堆硬幣* 對(duì)前兩堆稱重,相等則對(duì)第三堆繼續(xù)操作,不相等則對(duì)輕的一堆繼續(xù)操作* */if (n % 3 == 0) {x = n / 3;} else {x = n / 3 + 1;return f3(l+1, r);}int mid1 = l + x - 1;int mid2 = mid1 + x;if (sum(l, mid1) == sum(mid1+1, mid2)) {return f3(mid2+1, r);} else if (sum(l, mid1) < sum(mid1+1, mid2)){return f3(l, mid1);} else {return f3(mid1+1, mid2);}}/*** 獲取指定區(qū)域內(nèi)硬幣的重量* */private static int sum(int l, int r) {int sum = 0;for (int i = l; i <= r; i++) {sum += a[i];}return sum;} }
二分查找算法適用于單調(diào)的一個(gè)函數(shù),即數(shù)組序列要么升序,要么降序
而三分查找算法使用于凸函數(shù),常用來(lái)求極值問(wèn)題。
在假幣問(wèn)題中,三分查找在n較大的情況下,效率是優(yōu)于二分查找的。
最復(fù)雜的情況
下面來(lái)回到最開(kāi)始的問(wèn)題,在不知道假幣輕重的情況下,我們通過(guò)下面的算法來(lái)實(shí)現(xiàn),其時(shí)間復(fù)雜度為O(log(以2為底n的對(duì)數(shù)))
相比來(lái)說(shuō),這種情況思考起來(lái)比較復(fù)雜,但是好在邏輯清楚,只要理解了思路,算法還是好實(shí)現(xiàn)的,下面我們來(lái)舉一個(gè)例子,假設(shè)有八枚硬幣,其中有一枚硬幣是假幣。但是我們不知道假幣是比真幣重還是輕。先把八枚硬幣編號(hào),分別表示為a,b,c,d,e,f,g,h,從八枚硬幣中任取六枚a,b,c,d,e,f,在天平兩端各放三枚進(jìn)行比較。假設(shè)a,b,c三枚放在天平的一端,d,e,f三枚放在天平的另一端,可能出現(xiàn)三種比較結(jié)果:
⑴ a+b+c>d+e+f
⑵ a+b+c=d+e+f
⑶ a+b+c<d+e+f
若a+b+c>d+e+f,可以肯定這六枚硬幣中必有一枚為假幣,同時(shí)也說(shuō)明g、h為真幣。這時(shí)可將天平兩端各去掉一枚硬幣,假設(shè)去掉c、f,同時(shí)將天平兩端的硬幣各換一枚,假設(shè)硬幣b、e作了互換,然后進(jìn)行第二次比較,比較的結(jié)果同樣可能有三種:
① a+e>d+b:這種情況表明天平兩端去掉硬幣c、f且硬幣b、e互換后,天平兩端的輕重關(guān)系保持不變,從而說(shuō)明了假幣必然是a,d中的一個(gè),這時(shí)我們只要用一枚真幣(例如h)和a進(jìn)行比較,就能找出假幣。若a>h,則a是較重的假幣;若a=h,則d為較輕的假幣;不可能出現(xiàn)a<h的情況。(為什么?很簡(jiǎn)單,因?yàn)槲覀兣袛嗔薬,d中有一個(gè)假幣那么e,b都是真幣,則e=d。而a+e>d+b可以推出a>d,所以不管a是真是假都不可能出現(xiàn)a<h情況出現(xiàn))
② a+e=d+b:此時(shí)天平兩端由不平衡變?yōu)槠胶?#xff0c;表明假幣一定在去掉的兩枚硬幣c,f中,同樣用一枚真幣(例如h)和c進(jìn)行比較,若c>h,則c是較重的假幣;若c=h,則f為較輕的假幣;不可能出現(xiàn)c<h的情況。
③ a+e<d+b:此時(shí)表明由于兩枚硬幣b,e的對(duì)換,引起了兩端輕重關(guān)系的改變,那么可以肯定b或e中有一枚是假幣,同樣用一枚真幣(例如h)和b進(jìn)行比較,若b>h,則b是較重的假幣;若b=h,則e為較輕的假幣;不可能出現(xiàn)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;}}/*** 獲取指定區(qū)域內(nèi)硬幣的重量* */private static int sum(int l, int r) {int sum = 0;for (int i = l; i <= r; i++) {sum += a[i];}return sum;} }
總結(jié)
以上是生活随笔為你收集整理的减治法解决八枚硬币问题/假币问题(JAVA)----二分,三分,不知轻重的情况的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 判断一个数是否存在于一个非递减的有序数列
- 下一篇: Apache nifi 集群安装