英文:
How to Test a System Designed to Sort Four Numbers Using Only Five Comparisons?
问题
我有一个系统,可以使用五次比较来排序四个数字。该系统使用一系列名为“MinMax”的比较器箱,接受两个数字并输出最小值和最大值。我的配置如下:
- 我首先并行使用两个MinMax箱来比较a和b,以及c和d。
- 随后,将这些箱的最小值和最大值输出送入另外两个MinMax箱,以确定整体的最小值和最大值。
- 最后,使用一个最终的MinMax箱来比较第二小值和第二大值。
该系统旨在使用这五次比较来排序任何给定的四个数字序列。然而,我担心系统可能会出现故障 - 例如,如果其中一个MinMax箱出现故障。
我想设计一个测试,以确保系统的所有组件都正常运行。我的问题是:我可以输入哪个最小序列来验证系统的完整性,为什么选择这个序列?我寻找一个能够有效测试系统中的所有MinMax箱和连接的序列。
(有一个类似的问题询问如何排序,而不是如何测试。https://stackoverflow.com/questions/6145364/sort-4-number-with-few-comparisons)
英文:
I have a system to sort four numbers using a sequence of five comparisons. The system uses a series of 'MinMax' comparator boxes that take in two numbers and output the minimum and maximum. My configuration is as follows:
- I use two MinMax boxes in parallel to compare a & b and c & d initially.
- The minimum and maximum outputs from those boxes are then fed into two more MinMax boxes to determine the overall minimum and maximum.
- The second minimum and second maximum are then compared using a final MinMax box.
The system is designed to sort any given sequence of four numbers with these five comparisons. However, I am concerned about potential failures in the system - if one of the MinMax boxes were to malfunction, for instance.
I would like to devise a test to ensure that all components of the system are functioning correctly. My question is: What is the minimal sequence of numbers that I can input to verify the integrity of the system, and why those sequence? I'm looking for a sequence that would efficiently test all MinMax boxes and connections in the system.
(There is a similar question that ask how to sort, not how to test. https://stackoverflow.com/questions/6145364/sort-4-number-with-few-comparisons)
答案1
得分: 1
为了全面测试排序功能,您必须创建具有元素之间所有可能关系的输入样本。对于 N=4,一些示例可能如下所示:
{{C},{B},{A},{D}} → (C<B) & (B<A) & (A<D) → [2,1,0,3]
{{A,D},{B,C}} → (A=D) & (D<B) & (B=C) → [0,1,1,0]
{{B},{A,C,D}} → (B<A) & (A=C) & (C=D) → [1,0,1,1]
这里相同组的元素{}
是相等的,但比前面的组元素大。
相等组的有序分区总数为2^N-1,您必须全部处理。在您的情况下,将有8个分区:
{1,1,1,1}
{1,1,2}
{1,2,1}
{2,1,1}
{1,3}
{2,2}
{3,1}
{4}
因此,对于每个分区,您必须迭代所有可能的不相交的N个元素集合。例如,对于分区{2,2}
,有6种这样的组合:
{{A,B},{C,D}}
{{A,C},{B,D}}
{{A,D},{B,C}}
{{B,C},{A,D}}
{{B,D},{A,C}}
{{C,D},{A,B}}
然后,为了获得具体的数字,只需为第一组元素设置值为0,并逐渐递增,以创建N个元素的实际样本,每个样本对于给定的分区都会产生唯一的排序:
{{A,B},{C,D}} → [0,0,1,1]
{{A,C},{B,D}} → [0,1,0,1]
{{A,D},{B,C}} → [0,1,1,0]
{{B,C},{A,D}} → [1,0,0,1]
{{B,D},{A,C}} → [1,0,1,0]
{{C,D},{A,B}} → [1,1,0,0]
当您完成所有分区时,您将获得对您的算法的完整单元测试(前提是无论被排序的数字的具体算术属性如何,它都表现出一致的行为)。
如果您不想手动执行此操作,以下代码段将生成N=4的所有样本(共75个):
英文:
In order to test sorting function exhaustively, you have to create input samples with all possible relations between their elements.<br>For N=4 some samples might look like this:
{{C},{B},{A},{D}} → (С<B) & (B<A) & (A<D) → [2,1,0,3]
{{A,D},{B,C}} → (A=D) & (D<B) & (B=C) → [0,1,1,0]
{{B},{A,C,D}} → (B<A) & (A=C) & (C=D) → [1,0,1,1]
Here elements of the same group {}
are equal, but greater than elements of previous groups.
Total number of ordered partitions of equality groups is 2<sup>N-1</sup>, all of which you have to work through.<br>In your case there will be 8 partitions:
{1,1,1,1}
{1,1,2}
{1,2,1}
{2,1,1}
{1,3}
{2,2}
{3,1}
{4}
So for each partition, you have to iterate over all possible disjoint sets of N elements.<br>For example, for the partition {2,2}
there're 6 such combinations:
{{A,B},{C,D}}
{{A,C},{B,D}}
{{A,D},{B,C}}
{{B,C},{A,D}}
{{B,D},{A,C}}
{{C,D},{A,B}}
Then, in order to get concrete numbers, it's enough to set a value of 0 for the elements of the first group, and increase it by 1 for the elements of each subsequent group. This will create actual samples of N elements, each resulting in a unique ordering for a given partition:
{{A,B},{C,D}} → [0,0,1,1]
{{A,C},{B,D}} → [0,1,0,1]
{{A,D},{B,C}} → [0,1,1,0]
{{B,C},{A,D}} → [1,0,0,1]
{{B,D},{A,C}} → [1,0,1,0]
{{C,D},{A,B}} → [1,1,0,0]
When you're done with all partitions, you'll have a complete unit test for your algorithm (provided that it behaves coherently regardless of specific arithmetic properties of the numbers being sorted).
If you don't like doing it by hand, the following snippet will generate all samples for N=4 (75 in total):
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function generateSamples (length, callback) {
// test sample
let sample = new Uint32Array(length);
// doubly linked list of unused indices in sample
let unused = [];
let unusedHead = 0;
let unusedCount = length;
for (let i = 0; i < length; i++) {
unused.push({ index: i, prev: null, next: null });
}
for (let i = 1; i < length; i++) {
unused[i - 1].next = unused[i];
unused[i].prev = unused[i - 1];
}
// list of group sizes
let partition;
// flag for callback
let newPartition;
// recurse groups
function processNextGroup (groupId) {
// proceed to the next group
if (groupId < partition.length) {
processNextElement(groupId, partition[groupId], unused[unusedHead], unusedCount - partition[groupId] + 1);
}
// the final group is exhausted, the sample is complete
else if (newPartition) {
newPartition = false;
callback(sample.slice(), `{${partition.join(',')}}`);
}
else {
callback(sample.slice(), false);
}
}
// recurse group elements
function processNextElement (groupId, groupSize, indexItem, variants) {
// iterate over remaining variants for the current element
for (let i = 0; i < variants; i++, indexItem = indexItem.next) {
// unlink index item from the list
if (indexItem.prev) {
indexItem.prev.next = indexItem.next;
}
else {
unusedHead = indexItem.next?.index;
}
if (indexItem.next) {
indexItem.next.prev = indexItem.prev;
}
// update current sample element
sample[indexItem.index] = groupId;
// recurse...
unusedCount--;
if (groupSize > 1) {
// ... to the next element
processNextElement(groupId, groupSize - 1, indexItem.next, variants - i);
}
else {
// ... to the next group
processNextGroup(groupId + 1);
}
unusedCount++;
// return index item to the list
if (indexItem.prev) {
indexItem.prev.next = indexItem;
}
else {
unusedHead = indexItem.index;
}
if (indexItem.next) {
indexItem.next.prev = indexItem;
}
}
}
// generate partitions by iterating bitmaps
// - 1-bit: current element starts new group
// - 0-bit: current element continues the last group
let maxBitmap = 2 ** (length - 1);
for (let bitmap = 0; bitmap < maxBitmap; bitmap++) {
partition = [];
let groupSize = 1;
for (let pos = length - 2; pos >= 0; pos--) {
if ((bitmap >>> pos) & 1) {
partition.push(groupSize);
groupSize = 1;
}
else {
groupSize++;
}
}
partition.push(groupSize);
newPartition = true;
// generate every combination of the current partition
processNextGroup(0);
}
}
let lastSignature;
let samples;
let samplesAll = [];
function logSamples (caption, samples) {
console.log(caption);
console.log(`[\n [${samples.join('],\n [')}]\n]`);
}
generateSamples(4, (sample, newSignature) => {
if (newSignature) {
if (samples) {
logSamples(`Partition = ${lastSignature}`, samples);
}
lastSignature = newSignature;
samples = [];
}
samples.push(sample);
samplesAll.push(sample);
});
logSamples(`Partition = ${lastSignature}`, samples);
logSamples(`ALL SAMPLES: ${samplesAll.length}`, samplesAll);
<!-- end snippet -->
PS: The function generateSamples
can generate larger sets for whatever reasons. I tested it up to N=9 which yielded 7,087,261 combinations. Internally it uses O(N) space and generates each sample in O(N) time.
答案2
得分: 1
由于零一原则对于排序网络的适用性,如果你的排序算法可以看作是一个排序网络(正如你所称的MinMax框的组合几乎是排序网络的定义),那么只需检查所有的二进制输入就足够了,在这种情况下只有2<sup>4</sup>=16种可能。
这些输入对应于数字0到15(将作为4个单独的位输入到算法中),因此生成输入序列的算法非常简单。
英文:
Thanks to the zero-one principle of sorting networks, if your sorting algorithm can be viewed as a sorting network (a composition of MinMax boxes as you call them is pretty much the definition of a sorting network) it suffices to check all binary inputs, of which there are only 2<sup>4</sup>=16 in this case.
Those inputs correspond to the numbers 0..15 (which would be fed into the algorithm as 4 separate bits, not one number) so the algorithm to generate the sequence of inputs is trivial.
答案3
得分: 0
有4!= 24种(1,2,3,4)的排列。枚举它们很容易。如果您的系统将其中一些排序错误,那么它是有问题的。如果它正确排序了所有这些排列,我们可以声明它正确地排序了(1,2,3,4)的排列。这增强了对其正确运行的信心,但不能证明它。确实,如果您的系统中的一个比较箱比较了23002936894782192854和94528594871170264370,而且只有这一对排序不正确呢?要排除所有这种故障模式,需要检查所有4个数字的所有排列,而这些排列有无限多。
英文:
There are 4!=24 permutations of (1,2,3,4). It is easy to enumerate them. If your system sorts some of them incorrectly, then it is broken. If it sorts all of them correctly, we can claim that it correctly sorts permutations of (1,2,3,4). This inspires confidence in its correct functioning, but cannot prove it. Indeed, what if one of your boxes compares 23002936894782192854 and 94528594871170264370, and only this pair, incorrectly? It is impossible to weed out all such failure modes without checking all permutations of all 4-tuples of numbers, and there are infinitely many of those.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论