分治--测试芯片(JAVA)

问题描述:

将两片芯片放在测试台上相互测试(比如 A 芯片测试 B,B 芯片测试 A),并且给出测试报告是 “好” 或者 “坏”。

假设:好芯片报告一定是正确的;坏芯片报告不一定是正确的(比如有可能报告 “好”,也有可能报告 ”坏“)。

测试结果:
序号A的报告B的报告推测结果
B “好”A “好”两片都是好的或两片都是坏的
B “好”A “坏”至少有一片是”坏“的
B “坏”A ”好“至少有一片是”坏“的
B “坏”A “坏”至少有一片是”坏“的
对测试结果推导:
  1. 推导①:

    1. 假设A本身为“好”,则A报告正确,则B是“好”,此时二者都是“好”;
    2. 假设A本身为“坏”,而此时B报告说A是”好“,说明B是”坏“,而A报告说B是“好”,说明A也是“坏”,符合假设此时二者都是“坏”。
  2. 推导②:

    1. 假设A本身为”好“,则A报告正确,说明B是“好”,但B报告A为“坏”,与假设冲突;

    2. 假设A本身是”坏“,则B有可能是”好“也有可能是“坏”。

    3. 综上:此时一定是至少有一片是”坏“的。

  3. 推导③:

    1. 假设B本身为”好“,则B报告正确,说明A是“好”,但A报告B为“坏”,与假设冲突;
    2. 假设B本身是”坏“,则A有可能是”好“也有可能是“坏”。
    3. 综上:至少有一片是”坏“的。
  4. 推导④:

    1. 首先不可能两个都是”好“的;
    2. 假设A本身是”好“,则A报告正确,则B是”坏“,成立;
    3. 假设A本身是”坏“,则B有可能是”好“也有可能是“坏”。
    4. 综上:此时一定是至少有一片是”坏“的。
实例:
一、 题目要求:

输入芯片数目为n,以及n片芯片的状态(“好” 或者 ”坏“)。

假设:

  1. “好” 芯片状态输入为1;“坏” 芯片状态输入为0。
  2. ”好“ 芯片至少比 “坏” 芯片多一片。

通过测试从n片芯片中找到一片 ”好“ 芯片,并输出其编号(编号从 1 号开始),要求测试次数最少。

二、 分治思想:
1. 假设n为偶数时:

①. 测试方法:

将n片芯片分成n/2组,两两分成一组进行测试,并根据淘汰规则淘汰芯片,将留下来的芯片再进行分组测试,这样就将原问题分解为子问题了。

②. 淘汰规则

序号A的报告B的报告推测结果淘汰规则
B “好”A “好”两片都是好的或两片都是坏的任留一片进入下一轮
B “好”A “坏”至少有一片是”坏“的全部扔掉
B “坏”A ”好“至少有一片是”坏“的全部扔掉
B “坏”A “坏”至少有一片是”坏“的全部扔掉

**③. 注意:**根据上述淘汰规则进行递归的过程中为什么会一直保证 ”好“ 芯片永远要比 ”坏“ 芯片至少多一片?

④. 证明:假设分组后情况如下:

  1. 刚好有 i 组两片都是 ”好“ 的芯片被分在了一起,此时报告会出现第①种情况,所以扔一半留一半,剩 i/2 片;
  2. 刚好有 j 组 一”好“一“坏” 的芯片被分在了一起,此时报告会出现第②③④种情况,所以全扔,剩 0 片;
  3. 刚好有 k 组两片都是 “坏” 的芯片被分在了一起,此时报告会出现第①②③④种情况,所以会出现有的组全扔,而有的组扔一片留一片的情况,因此最多会剩 k/2片(此时 k 组全部报告为第①种情况)。

因此,根据上述淘汰规则进行递归的过程中会一直保证 ”好“ 芯片永远要比 ”坏“ 芯片至少多一片

初始芯片数为:2i + 2j + 2k = n
初始分组后:”好“ “坏”
2i + j > 2k + j
经过一轮淘汰后剩余的芯片数:i > k

至此,根据 ”好“ 芯片永远要比 ”坏“ 芯片至少多一片 可以得到递归的截至条件为:芯片数 n≤2,此时,剩余的芯片就都是好芯片,此时就不用再递归。

2. 假设n为奇数时:

①. 存在的问题:

当n为奇数时,会出现最后一片芯片轮空,如果将轮空芯片直接进入下一轮,会将 ”好“ 芯片永远要比 ”坏“ 芯片至少多一片 这个条件破坏,比如 n=7时,由下图可知此时经过一轮淘汰后出现了留下来的芯片出现了 ”好“ 芯片数和 “坏” 芯片数相等的情况,所以,对轮空芯片不能采用直接进入下一轮的方法。
请添加图片描述
②. 解决方法:

将n片芯片分成n/2组,两两分成一组进行测试时,出现最后一片芯片轮空。此时,让轮空芯片去测试所有的前 n-1个芯片,这样会得到n-1个报告,根据报告判断轮空芯片是否留下。

假设n=7时,第7片芯片轮空,此时,让第7片芯片测试前6片芯片,这样会得到6份报告:

对6份报告推导结果推广到 n(n为奇数)片芯片的情况
当6份报告中至少3个报 “好” 时轮空芯片为 “好”至少有**(n-1)/2** 份报告 “好”,轮空芯片为 “好”
当6份报告中至少4个报 “坏” 时轮空芯片为 “坏”至少有**(n+1)/2** 份报告 “坏”,轮空芯片为 “坏”

对于轮空芯片的淘汰规则:当轮空芯片为 “好” 时,将轮空芯片留下,进入下一轮;当轮空芯片为 “坏” 时,将轮空芯片扔掉。这样在递归过程中 ”好“ 芯片永远要比 ”坏“ 芯片至少多一片 这一条件就不会被破坏。

代码实现:
import java.util.Scanner;

public class 分治_6芯片测试问题
{
	// 当n是奇数时,将前n - 1个芯片分别和轮空的芯片(第n个芯片)进行测试
	// X为前n-1个芯片,Y为轮空的芯片(第n个芯片)
	public static boolean Jtest(int X, int Y)
	{
		// 如果X为坏,Y为好,则返回坏
		if (X == 0 && Y == 1)
			return false;
		// 否则,如果X为好,Y为好,则返回好
		else if (X == 1 && Y == 1)
			return true;
		// 如果Y为坏,则返回一个随机值
		else
			return ((int) Math.random() * 2 == 1);
	}

	// 模拟测试台,两两分组后进行测试
	public static boolean[] test(int A, int B)
	{
		// result 用来存测试结果
		boolean[] result = new boolean[2];

		// 假设刚好都为好的芯片分在一组时的结果
		if (A == 1 && B == 1)
		{
			result[0] = true;
			result[1] = true;
		}

		// 假设刚好一好一坏的芯片分在一组时的结果
		if ((A == 1 && B == 0) || (A == 0 && B == 1))
		{
			result[0] = false;
			result[1] = ((int) Math.random() * 2 == 1);
		}

		// 假设刚好都为坏的芯片分在一组时的结果
		if (A == 0 && B == 0)
		{
			result[0] = ((int) Math.random() * 2 == 1);
			result[1] = ((int) Math.random() * 2 == 1);
		}

		return result;
	}

	//
	public static int[][] getRight(int[][] arr, int length)
	{
		// 截至条件:当前芯片数 length≤2 ,此时剩余的芯片就都是好芯片。
		if (length <= 2)
		{
			int[][] a = new int[length][length];
			for (int i = 0; i < length; i++)
			{
				a[i] = arr[i];
			}
			return a;
		} else // 此时当前芯片数 length≥3 时,没有到达截至条件
		{
			// temp 数组用来存放进入下一轮的芯片
			int[][] temp = new int[(length / 2) + 1][(length / 2) + 1];
			// result 数组用来存每次测试后返回的报告
			boolean[] result = null;
			// count 表示进入下一轮测试的芯片数
			int count = 0;

			// 当前芯片数length为偶数
			for (int i = 0; i < length - 1; i = i + 2)
			{
				// 两两一组进行测试
				result = test(arr[i][1], arr[i + 1][1]);
				// 如果报告出现两个都是 “好”,则任留一个进入下一轮,否则,全部扔掉
				if (result[0] == true && result[1] == true)
				{
					int x = (int) (Math.random() * 2);
					// 将留下的芯片的编号和状态(“好”或者“坏”)都存入temp数组中
					temp[count] = arr[i + x];
					// 由于用来存芯片的数组是一个二维数组,所以每次留一个芯片时,count要+2
					count = count + 2;
				}
			}

			// 当芯片数为奇数时,会出现有一个芯片轮空,让空芯片测试前length-1
			if (length % 2 == 1)
			{
				// jishu 数组用来存轮空芯片对前length-1个芯片测试的报告
				boolean[] jishu = new boolean[length - 1];
				for (int i = 0; i < length - 1; i++)
					jishu[i] = Jtest(arr[i][1], arr[length - 1][1]);

				// True 用来记录报告为“好”的数量
				int True = 0;
				for (int i = 0; i < length - 1; i++)
				{
					if (jishu[i] == true)
						True++;
				}

				// 当 True≥(length-1)/2 时,说明轮空芯片为“好”,将轮空芯片留下,进入下一轮;否则扔掉
				if (True >= (length - 1) / 2)
				{
					temp[count] = arr[length - 1];
					count = count + 2;
				}
			}
			// 递归调用,进入下一轮
			return getRight(temp, count / 2);
		}
	}

	public static void main(String[] args)
	{
		// TODO 自动生成的方法存根
		Scanner input = new Scanner(System.in);
		// 输入初始芯片数 n
		int n = input.nextInt();
		// 用数组模拟一个n行2列的表,第一行存芯片对应的编号,第2列存芯片的状态(“好”或者“坏”)
		int[][] arr = new int[n][2];

		if (n == 1)
			System.out.println("最先找到的 “好” 芯片编号(从1号开始):" + '1' + "号");
		else
		{
			for (int i = 0; i < n; i++)
			{
				arr[i][0] = i + 1;
				arr[i][1] = input.nextInt();
			}
			input.close();

			int[][] temp = null;
			temp = getRight(arr, n);
			System.out.println("最先找到的 “好” 芯片编号(从1号开始):" + temp[0][0] + "号");
		}
	}
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值