12球稱重問題思維分析(updated #1)

昨天又從某處看到了12球稱重的問題,雖然之前有過解題的過程,但這次仍然費了不少時間才又找到解題方法。然後我又思考,從我看到問題,到得到解決方法,這是怎樣的一個過程,怎樣的思維想到了怎樣一個方法,從而得到了怎樣的一個結果。假如我能夠把這個過程描述下來,那可謂對自己的思維過程有了一個很好的梳理,並且可以記錄入自己的SOP;同時,也就更加真實地瞭解自己。而更爲重要的是,可以將這種思維的過程,傳播出去;從而不僅僅是知識得到延續,而更爲重要的思維過程,也可以得到延續。

題目:12個球,外觀(大小、色澤等)完全一致,其中,有十一個球的重量是一樣的,另一個球的重量與其他球是不一致的。怎樣用天平最多僅稱重三次,找出那個重量不一致的球,並且還能確認這個球是較重還是較輕。

這個題的重點是不知道那個不一致的球是重了還是輕了。那假如是這個球是重的呢?

很多人的思路是,平分成兩組,每組6個,進行稱重。然後將重的一組再分組,每組3個,進行稱重。最後,將重的一組(3個球)其中兩個稱重,若有一個重,則這個球是要找的球;若相等,則沒有稱重的這個球是要找的球。

但是這個過程是無法直接應用到要解決的問題上的。所以,只能換一個方向——簡化。

兩個球的情況:無法得知哪個球是要找的球。

三個球的情況:標記ABC,取AB進行稱重,會有兩種情況:

1,它們相等,那麼C就是要找的球,稱A和C

1.1 C>A,C較重。

1.2 C<A,C較輕

1.3 C=A,A=B=C,與前提矛盾,因此不可能出現。

2,它們不等,我們假設A>B,那麼C是一顆正常的球。將A與C稱重,

2.1相等,B球較輕,爲要找的球。

2.2A>C,A球較重,爲要找的球。

2.3A<C,那麼C>A>B,與前提矛盾,因此不可能出現。

然後再回過頭來,看上面的過程。你會發現什麼,先想一想吧……

這其實是一個信息獲取的過程,根據已知信息,通過操作,得到未知信息。如果我們一直測AB的話,即使測100遍也無法找到重量較輕或者較重的那個球。

可以通過一種標記來記錄信息,這裏球有正常,較輕和較重三種情況,可以用(+1,0,-1)來表示。當通過一次次的測量,爲各個球標上(+1,0,-1)三種狀態,並最終有且僅有一個球的狀態爲+1或者-1,就可以確定,這個狀態爲+1或者-1的球爲要找的較重或者較輕的球。爲上述操作過程分別標記信息含量值

1,它們相等,那麼C就是要找的球[A0,B0,C]

稱A和C

1.1 A<C,C重[A0,B0,C+]

1.2 A>C,C輕[A0,B0,C-]

1.3 C=A,A=B=C,與前提矛盾,因此不可能出現。

2,它們不等,我們假設A>B,那麼C是一顆正常的球。[A+,B-,C0]

將A和C

2.1 A=C,B輕[A0,B-,C0]

2.2 A>C,A重。可由A>C和前提得知B=C,因此[A+,B0,C0]

2.3 A<C,那麼C>A>B,與前提矛盾,因此不可能出現。

那麼,總結一下整個過程,就是將球分爲較重、較輕(標記+-),並通過和標準球(標記0)的比較,來得知是否爲重球(不是重球,則與之相比較的球是輕球)或者輕球。

再來分析一下標記的信息值,0表示爲標準球,含有確切含義,而+和-在不唯一的情況下,只能表示可能較重,但其實並不重,按其信息值排序的話:0>(+或-)>沒有標記,我們的目的是在每次稱重的過程中,使獲得的信息值最大化。

12球的過程

6:6稱重,只能將6個球標記爲+,另6個球標記爲-,無法得到0的標記

而4:4稱重,至少可以將4個球標記爲0(其實,有兩種可能,一是8個0,另一種是4個+,4個-,4個0)

(未完待續)
4:4稱重
1.  兩邊相平
     1~8標記爲0
     將9,10放左邊,11和1放右邊,稱重
1.1 左邊重
     9,10:+
     11:-
     12:0
     對9,10進行稱重,便可得到結果
1.1.1 9=10,則9,10:0,11爲較輕的球
1.1.2 9>10,則9爲較重的球
1.1.3 9<10,則10爲較重的球

1.2 右邊重
     9,10:-
     11:+
     12:0
     同1.1剩餘部分

1.3 同樣重
      則9,10,11標記爲0,取1和12稱重,因爲1是標準球,根據天平狀態,可以得出12球是輕還是重

2.  左邊重
     1~4:+
     5~8:-
     9~12:0
     選擇1,2,5,6和3,7,9,10稱重
2.1 1,2,5,6=3,7,9,10
     1,2,3,5,6,7:0
     4:+
     8:-
     隨便用4或者8跟其他球稱一些就可得到結果
2.2 1,2,5,6>3,7,9,10
     1,2:+
     7:-
     3,4,5,6,8:0
     1和2再稱一次就可得到結果
2.3 1,2,5,6<3,7,9,10
     5,6:-
     3:+
     1,2,4,7,8:0
     5和6再稱一次就可得到結果
3.  右邊重,同2

這個遊戲如果用程序來實現的話,有兩種實現方法:
一種是不考慮過程的,讓玩家選出的球和初始化時隨機選中的球比較,這個實現非常簡單,但是會存在運氣的成分,讓玩家有機率蒙對。
另一種是考慮過程的,根據玩家的選球稱重狀態來判斷最後的結果,這樣避免了運氣成分。

平衡情況:
遊戲開始:
遊戲示例,第二個球比較重請輸入:Y;2;1
遊戲示例,第十個球比較輕請輸入:Y;10;-1
遊戲示例,左右兩邊分別稱1,2,3,4和5,6,7,8請輸入:N;1,2,3,4;5,6,7,8
N;1,2,3,4;5,6,7,8
左邊輕,右邊重
1:-1;  2:-1;  3:-1;  4:-1;  5:1;  6:1;  7:1;  8:1;  9:0;  10:0;  11:0;  12:0; 
N;1,2,5,6;3,7,9,10
左邊重,右邊輕
1:0;  2:0;  3:-1;  4:0;  5:1;  6:1;  7:0;  8:0;  9:0;  10:0;  11:0;  12:0; 
N;5;6
左邊重,右邊輕
1:0;  2:0;  3:0;  4:0;  5:1;  6:0;  7:0;  8:0;  9:0;  10:0;  11:0;  12:0; 
Y;5;1
你真厲害,你答對了!
1:0;  2:0;  3:0;  4:0;  5:1;  6:0;  7:0;  8:0;  9:0;  10:0;  11:0;  12:0;  

不平衡情況:
遊戲開始:
遊戲示例,第二個球比較重請輸入:Y;2;1
遊戲示例,第十個球比較輕請輸入:Y;10;-1
遊戲示例,左右兩邊分別稱1,2,3,4和5,6,7,8請輸入:N;1,2,3,4;5,6,7,8
N;1,2,3,4;5,6,7,8
兩邊平衡
1:0;  2:0;  3:0;  4:0;  5:10;  6:10;  7:10;  8:10;  9:10;  10:10;  11:10;  12:10; 
N;9,10;11,1
左邊輕,右邊重
1:0;  2:0;  3:0;  4:0;  5:0;  6:0;  7:0;  8:0;  9:-1;  10:-1;  11:1;  12:0; 
N;9;10
左邊重,右邊輕
1:0;  2:0;  3:0;  4:0;  5:0;  6:0;  7:0;  8:0;  9:0;  10:-1;  11:0;  12:0; 
Y;10;-1
你真厲害,你答對了!
1:0;  2:0;  3:0;  4:0;  5:0;  6:0;  7:0;  8:0;  9:0;  10:-1;  11:0;  12:0;  


運氣情況:
遊戲開始:
遊戲示例,第二個球比較重請輸入:Y;2;1
遊戲示例,第十個球比較輕請輸入:Y;10;-1
遊戲示例,左右兩邊分別稱1,2,3,4和5,6,7,8請輸入:N;1,2,3,4;5,6,7,8
Y;1;1
哈哈,你開始猜了。
你運氣太好了,竟然猜對了!
1:10;  2:10;  3:10;  4:10;  5:10;  6:10;  7:10;  8:10;  9:10;  10:10;  11:10;  12:10;  


簡單實現代碼如下:

package com.liubuhu.twevel;

import java.util.Scanner;

public class TwevelBalls {
	private static final int LIGHT = -1;
	private static final int NORMAL = 0;
	private static final int HEAVY = 1;

	private Integer[] balls = null;

	public Integer[] getBalls() {
		if (balls == null) {
			balls = new Integer[12];
			for (int i = 0; i < balls.length; i++) {
				balls[i] = 10;
			}
		}
		return balls;
	}

	public TwevelBalls() {
	}

	private class InputBean {
		private Boolean bfind;
		private Integer ball;
		private Integer heavy;
		private Integer[] left;
		private Integer[] right;

		public Boolean getBfind() {
			return bfind;
		}

		public void setBfind(Boolean bfind) {
			this.bfind = bfind;
		}

		public Integer getBall() {
			return ball;
		}

		public void setBall(Integer ball) {
			this.ball = ball;
		}

		public Integer[] getLeft() {
			return left;
		}

		public void setLeft(Integer[] left) {
			this.left = left;
		}

		public Integer[] getRight() {
			return right;
		}

		public void setRight(Integer[] right) {
			this.right = right;
		}

		public void setHeavy(Integer heavy) {
			this.heavy = heavy;
		}

		public Integer getHeavy() {
			return heavy;
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		TwevelBalls t = new TwevelBalls();
		t.run();
	}

	private void run() {
		Integer theBall = (int) Math.floor(Math.random() * 12);
		// 1 for heavy, -1 for light
		Integer heavy;
		if (Math.random() > 0.5) {
			heavy = HEAVY;
		} else {
			heavy = LIGHT;
		}
		Integer[] myBalls = getBalls();

		System.out.println("遊戲開始:");
		System.out.println("遊戲示例,第二個球比較重請輸入:Y;2;1");
		System.out.println("遊戲示例,第十個球比較輕請輸入:Y;10;-1");
		System.out.println("遊戲示例,左右兩邊分別稱1,2,3,4和5,6,7,8請輸入:N;1,2,3,4;5,6,7,8");
		InputBean bean = null;
		Scanner scan = new Scanner(System.in);
		// 1
		bean = getInput(scan);
		myBalls = processData(myBalls, bean, theBall, heavy);
		// 2
		bean = getInput(scan);
		myBalls = processData(myBalls, bean, theBall, heavy);
		// 3
		bean = getInput(scan);
		myBalls = processData(myBalls, bean, theBall, heavy);
		// 4 結果
		bean = getInput(scan);
		myBalls = processData(myBalls, bean, theBall, heavy);
		// 結束
		scan.close();
		// System.out.println("遊戲結束,開始頒獎");
	}

	private Integer[] processData(Integer[] myBalls, InputBean bean,
			Integer theBall, Integer heavy) {
		if (bean.getBfind()) {
			if (check(myBalls)) {
				if (theBall.equals(bean.getBall())
						&& heavy.equals(bean.getHeavy())) {
					System.out.println("你真厲害,你答對了!");
				} else {
					System.out.println("差了一點點,你是不是敲錯鍵盤了");
				}
			} else {
				if (theBall.equals(bean.getBall())
						&& heavy.equals(bean.getHeavy())) {
					System.out.println("你運氣太好了,竟然猜對了!");
				} else {
					System.out.println("Sorry,你猜錯了!");
				}
			}
		} else {
			myBalls = processBalls(myBalls, bean, theBall, heavy);
		}
		outputBalls(myBalls);
		return myBalls;
	}

	private void outputBalls(Integer[] myBalls) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < myBalls.length; i++) {
			sb.append(Integer.valueOf(i + 1).toString()).append(":").append(
					Integer.valueOf(myBalls[i]).toString()).append(";  ");
		}
		System.out.println(sb.toString());
	}

	private Integer[] processBalls(Integer[] myBalls, InputBean bean,
			Integer theBall, Integer heavy) {
		// 左邊有特殊球
		if (inTheArray(bean.getLeft(), theBall)) {
			if (heavy.equals(Integer.valueOf(0))) {
				// 左邊重,右邊輕
				myBalls = labelBalls(myBalls, bean, HEAVY);
				System.out.println("左邊重,右邊輕");
			} else {
				// 左邊輕,右邊重
				myBalls = labelBalls(myBalls, bean, LIGHT);
				System.out.println("左邊輕,右邊重");
			}
			// 右邊有特殊球
		} else if (inTheArray(bean.getRight(), theBall)) {
			if (heavy.equals(Integer.valueOf(0))) {
				// 左邊輕,右邊重
				myBalls = labelBalls(myBalls, bean, LIGHT);
				System.out.println("左邊輕,右邊重");
			} else {
				myBalls = labelBalls(myBalls, bean, HEAVY);
				// 左邊重,右邊輕
				System.out.println("左邊重,右邊輕");
			}
		}
		// 沒有特殊球
		else {
			myBalls = labelBalls(myBalls, bean, 0);
			System.out.println("兩邊平衡");
		}
		return myBalls;
	}

	private Integer[] labelBalls(Integer[] myBalls, InputBean bean, int l) {
		boolean[] labeled = new boolean[myBalls.length];
		for (int i = 0; i < myBalls.length; i++) {
			labeled[i] = false;
		}
		switch (l) {
		case NORMAL:
			for (int i = 0; i < bean.getLeft().length; i++) {
				myBalls[bean.getLeft()[i]] = NORMAL;
			}
			for (int i = 0; i < bean.getRight().length; i++) {
				myBalls[bean.getLeft()[i]] = NORMAL;
			}
			break;
		case HEAVY:// 左邊重,右邊輕
			for (int i = 0; i < bean.getLeft().length; i++) {
				if (myBalls[bean.getLeft()[i]] == LIGHT
						|| myBalls[bean.getLeft()[i]] == NORMAL) {
					myBalls[bean.getLeft()[i]] = 0;
				} else {
					myBalls[bean.getLeft()[i]] = HEAVY;
				}
				labeled[bean.getLeft()[i]] = true;
			}
			for (int i = 0; i < bean.getRight().length; i++) {
				if (myBalls[bean.getRight()[i]] == HEAVY
						|| myBalls[bean.getRight()[i]] == NORMAL) {
					myBalls[bean.getRight()[i]] = NORMAL;
				} else {
					myBalls[bean.getRight()[i]] = LIGHT;
				}
				labeled[bean.getRight()[i]] = true;
			}
			for (int i = 0; i < myBalls.length; i++) {
				if (labeled[i]) {

				} else {
					myBalls[i] = 0;
				}
			}
			break;
		case LIGHT:// 左邊輕,右邊重
			for (int i = 0; i < bean.getLeft().length; i++) {
				if (myBalls[bean.getLeft()[i]] == HEAVY
						|| myBalls[bean.getLeft()[i]] == NORMAL) {
					myBalls[bean.getLeft()[i]] = NORMAL;
				} else {
					myBalls[bean.getLeft()[i]] = LIGHT;
				}
				labeled[bean.getLeft()[i]] = true;
			}
			for (int i = 0; i < bean.getRight().length; i++) {
				if (myBalls[bean.getRight()[i]] == LIGHT
						|| myBalls[bean.getRight()[i]] == NORMAL) {
					myBalls[bean.getRight()[i]] = NORMAL;
				} else {
					myBalls[bean.getRight()[i]] = HEAVY;
				}
				labeled[bean.getRight()[i]] = true;
			}
			for (int i = 0; i < myBalls.length; i++) {
				if (labeled[i]) {

				} else {
					myBalls[i] = NORMAL;
				}
			}
			break;
		}
		return myBalls;
	}

	private boolean inTheArray(Integer[] array, Integer theBall) {
		for (int i = 0; i < array.length; i++) {
			if (theBall.equals(array[i])) {
				return true;
			}
		}
		return false;
	}

	private boolean check(Integer[] myBalls) {
		boolean retVal = true;
		int count = 0;
		for (int i = 0; i < myBalls.length; i++) {
			if (!Integer.valueOf(0).equals(myBalls[i])) {
				count++;
			}
		}
		if (count > 1) {
			retVal = false;
			System.out.println("哈哈,你開始猜了。");
		}
		return retVal;
	}

	private InputBean getInput(Scanner scan) {
		InputBean bean = new InputBean();
		String input = scan.nextLine();
		String[] splitedStr = input.split(";");
		if ("Y".equals(splitedStr[0])) {
			bean.setBfind(Boolean.TRUE);
		} else {
			bean.setBfind(Boolean.FALSE);
		}
		if (bean.getBfind()) {
			// 進行-1處理,使符合數組
			bean.setBall(Integer.valueOf(splitedStr[1]) - 1);
			bean.setHeavy(Integer.valueOf(splitedStr[2]));
		} else {
			bean.setLeft(gnrtBallNumbs(splitedStr[1]));
			bean.setRight(gnrtBallNumbs(splitedStr[2]));
		}
		return bean;
	}

	/**
	 * @param str
	 * @return
	 * @author liuhao
	 * @description : 進行-1處理,使符合數組
	 */
	private Integer[] gnrtBallNumbs(String str) {
		String[] splitedStr = str.split(",");
		Integer[] ballNumbs = new Integer[splitedStr.length];
		for (int i = 0; i < splitedStr.length; i++) {
			ballNumbs[i] = Integer.valueOf(splitedStr[i]) - 1;
		}
		return ballNumbs;
	}

}




 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章