从有限列表中简单选择

时间:2014-03-04 16:32:21

标签: c++

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

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或switch,或者switch / if combo:

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 ++解决方案。

有什么想法吗?

5 个答案:

答案 0 :(得分:1)

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

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或任何其他条件陈述的优雅解决方案......

答案 1 :(得分:1)

修改

第二眼看,首先选择唯一元素更有意义,其次是其他两个元素。这种方式更干净,但可能仍然不优雅。

如果你坚持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

答案 2 :(得分:1)

你真的只有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循环对我来说似乎很好。

答案 3 :(得分:0)

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

您必须注意列表为空时并相应地处理它。

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

答案 4 :(得分:0)

也许是这样的:

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