从有限列表进行简单选择

Simple selection from finite list

本文关键字:简单选择 列表      更新时间:2023-10-16

我有三个变量需要以某种方式设置。 例如

int a, b, c;
a = choose(1, 2,  3);   // a can take the value 1 to 3 inclusive
b = choose(1, 2,  3);   // b can take the value 1 to 3 inclusive
c = ??????              // c can't take either of the values in a or b.

我能想到的设置 c 的最简单方法是使用循环:

do
{
    c = choose(1, 2,  3);
}
while(c == a || c == b);

或者,我可以使用 ifs 或开关,或开关/if 组合:

a = choose(1, 2,  3);   
b = choose(1, 2,  3);   
switch(a){
    case 1:
        switch(b){
            case 1:
                c = choose(2, 3)
                break;
            case 2:
                c = 3;
                break;
            case 3:
                c = 2;
                break;
        }
        break;
    case 2:
        …

这些看起来都不优雅,后者只是血腥丑陋。

在一个项目中遇到了另一个类似的情况,我使用了std::setset_difference,但我不能在这里使用它,并且必须使用一个不太像 STL 和更老派C++解决方案。

有什么想法吗?

Edit

再看一眼,首先选择独特的元素,然后再选择其他两个元素更有意义。这样更干净,虽然可能仍然不优雅。

如果您坚持使用 choose 的离散变量实现,它可能看起来像这样:

int choose(int, int); // two choice overload
int choose(int, int, int); // three choice overload
int main()
{
    int c = choose(1, 2, 3);
    int x = 1 + c % 3;
    int y = 1 + (c + 1) % 3;
    int a = choose(x, y);
    int b = choose(x, y);
}

使用数组甚至更简洁,可以毫不费力地变得更加通用:

int choose(int[], int); // takes an array and its (effective) size
int main()
{
    constexpr int maxNum = 3;
    int choices[maxNum] = {1, 2, 3};
    //for larger values of maxNum, loop initialize:
    //for(int i = 0; i < maxNum; i++)
    //{
    //    choices[i] = i + 1;
    //}
    int c = choose(choices, maxNum);
    choices[c-1] = maxNum;
    int a = choose(choices, maxNum - 1);
    int b = choose(choices, maxNum - 1);
}
<小时 />

对于仅三个变量,我将采用最简单的方法并使用您发布的while循环:

do
{
    c = choose(1, 2,  3);
}
while(c == a || c == b);

如果您确实需要确定性运行时的保证,您可以尝试如下方法。但是,我不确定每个人都会觉得它更"优雅",而且我认为可维护性打击不值得。

if(a != b)
{
    c = 6 - (a + b);
}
else
{
    c = choose(0, 1);
    c = 1 + (a + c) % 3;
}

考虑到问题的设置方式,也不是那么可扩展,但在需要使其更普遍之前,我会假设 YAGNI。

您可以将其视为第二个解决方案的改进(优雅方面):

switch ((1<<a)|(1<<b))
{
    case  2: // a,b == 1,1
        c = choose(2,3);
        break;
    case  4: // a,b == 2,2
        c = choose(1,3);
        break;
    case  6: // a,b == 1,2 or 2,1
        c = 3;
        break;
    case  8: // a,b == 3,3
        c = choose(1,2);
        break;
    case 10: // a,b == 1,3 or 3,1
        c = 2;
        break;
    case 12: // a,b == 2,3 or 3,2
        c = 1;
        break;
}

您可以通过映射两个值来进一步改进它,对于每个值(1<<a)|(1<<b)

static int first[]  = {2,1,3,1,2,1};
static int second[] = {3,3,3,2,2,1};
int index = ((1<<a)|(1<<b))/2-1; // reduce from [2,4,6,8,10,12] to [0,1,2,3,4,5]
c = choose(first[index],second[index]);

在那里,您有一个优雅的解决方案,没有switchwhile或任何其他条件语句...

你真的只有 3 个这样的值还是这是一个最小化的例子?对于这种情况,我会直接排除取值

int a, b, c;
a = choose(1, 2,  3);   // a can take the value 1 to 3 inclusive
b = choose(1, 2,  3);   // b can take the value 1 to 3 inclusive
c = 1;
if(a == c || b == c)
  c = 2;
if(a == c || b == c)
  c = 3;

如果值为 1,2,3,您可以将其替换为与您的c++类似的 while 循环:

c = 1;
while(a == c || b == c)
    c++;

如果需要c chosen您提出的 while 循环对我来说似乎很好。

也许是这样的:

int a, b, c;
int myList[]= {1, 2, 3};
a = choose(myList);   // if choose can take a list as input
b = choose(myList);
myList.remove(a);
myList.remove(b);
c = choose(myList);  // the selection from a and b are out of the list at this point

此解决方案可确保

a != b && a != c 
一个简单的

解决方案是将链表(通过引用)传递到choose函数中。 所选元素将从列表中删除并返回。 对该函数的后续调用将有越来越少的元素可供选择。

您必须记下列表何时为空并相应地处理它。

如果允许ab相同,则choose将不是您用于确定c的函数。 在这种情况下,有一个单独的函数chooseC,该函数采用所选值的列表(通过常量引用)和可用值的列表(按值)。 从可用值中删除所选值,然后从剩余值中进行选择。