如何在假币算法中称重

Posted

技术标签:

【中文标题】如何在假币算法中称重【英文标题】:How to Weigh in Fake Coin Algorithm 【发布时间】:2017-03-21 22:23:43 【问题描述】:

我正在尝试为假币问题开发 C++ 代码。我正在使用一个长满 1 和一个随机 0 的二进制数组 n 来表示假硬币。当我将数组分成两半来比较权重时,如何确定每一边的权重?

我可以很容易地计算每个数组中 1 的数量,但这将是一个线性运行时间。整体算法应该是次线性的。

有没有办法在常数时间内确定每个数组的权重?

披露:这是一项学校作业,因此希望您可以在不给出完整答案的情况下给出提示。

【问题讨论】:

这更像是一个数学问题而不是一个软件问题。最好在数学 QA 处提问。 geeksforgeeks.org/decision-trees-fake-coin-puzzle 这解释了你的问题。看起来最佳解决方案是 log3(N),它是次线性的 你有错误的数据结构。您的数据结构应该有一个秘密编号 - 假币的 id,仅此而已。然后您可以回答“coin_set1coin_set2 重?”这样的问题。在 O(1) 中。 【参考方案1】:

作为一名学生,我会坚持这一点,因为您计算每 一半 中 1 的数量,并且您正在有效地使用这种方法:

减少和征服

因此,您实际上是在称重 O(log2 n) 次,这使得算法是次线性的。

我的观点是,优化算法的是您正在执行的称量次数及其有效空间(一半与总数)。

阅读此 CS session 中的更多信息,其中建议如果您在 3 处拆分,您可以做得更好并达到 O(log3 n)。但是,如果您对此不熟悉,那么现在分成两半就可以了! =)


如果你想玩弄一些代码,你可以使用std::bitset,它提供了count(),它返回位集中1的数量。

【讨论】:

【参考方案2】:

我可以很容易地计算每个数组中 1 的数量,但这将是线性运行时间。整体算法应该是次线性的。

我认为你在这里弄错了。 整个算法不可能可能是次线性的,原因很容易理解:要读取每个硬币至少需要线性时间,因为显然有线性数量的硬币的数量。很明显,如果不至少阅读每枚硬币一次,您就无法找到假硬币。 (更严格地说,您需要阅读所有硬币,除了一个至少一次,但这仍然是线性的。)

因此,其他答案和 cmets 的建议是,您需要称重硬币的次线性次数,我倾向于同意这一点。

如果您仍想更快地执行比较(即对范围求和),则有一个名为 Fenwick tree 的数据结构,它允许您以对数时间计算子范围和,但仍需要线性时间来构建。但是请注意,我认为将它用于您的任务在复杂性(它可能比您当前的水平更高级)和性能奖励(只有在您将要拥有许多range 查询在您的阵列上运行并且您将更改您的阵列)。

还请注意,wiki 文章的第二段建议了一种简单的算法,该算法只需要线性时间进行初始化,然后在恒定时间内计算前缀和(以及范围和):只需再创建一个前缀和为的数组您的硬币(如果您不再需要它,甚至可以就地更新原始硬币)。显然RangeSum(a,b) = PrefixSum(b) - PrefixSum(a-1)(其中PrefixSum(-1) 为0)。如果你想炫耀,实现这个可能是有意义的,但在 Big-O 方面仍然没有性能奖励。

【讨论】:

以上是关于如何在假币算法中称重的主要内容,如果未能解决你的问题,请参考以下文章

12枚硬币,怎么3次称出假币

分治算法理解

分治算法在 O(logn) 中找到假币

基础算法之二——枚举法

算法系列之枚举称硬币

如何在 Unity 3D 中使用称重秤?