在这种情况下,我将如何遍历所有各种可能性?

Posted

技术标签:

【中文标题】在这种情况下,我将如何遍历所有各种可能性?【英文标题】:How would I cycle through all of the various possibilities in this situation? 【发布时间】:2014-01-21 21:33:36 【问题描述】:

我看到一个我决定尝试的编程作业,它基本上是用户输入“123456789=120”之类的内容,程序必须在不同的位置插入一个“+”或“-”来生成语句真的。例如,在这种情况下,它可以做到 123+4-5+6-7+8-9 = 120。只有 3^8 种可能的组合,所以我认为暴力破解是可以的,但我没有'不确切知道我可以按什么顺序进入/如何实际实施。更具体地说,我不知道插入“+”和“-”的顺序是什么。这是我所拥有的:

#include <iostream>
#include <cmath>

using namespace std;

int string_to_integer(string);

int main()

    string input, result_string;
    int result, possibilities;

    getline(cin, input);

    //remove spaces
    for(int i = 0; i < input.size(); i++)
    
        if(input[i] == ' ')
        
            input.erase(i, 1);
        
    

    result_string = input.substr(input.find('=') + 1, input.length() - input.find('='));
    result = string_to_integer(result_string);
    input.erase(input.find('='), input.length() - input.find('='));

    possibilities = pow(3, input.length() - 1);
    cout << possibilities;



int string_to_integer(string substring)

    int total = 0;
    int power = 1;

    for(int i = substring.length() - 1; i >= 0; i--)
    
        total += (power * (substring[i] - 48));
        power *= 10;
    

    return total;

【问题讨论】:

以三进制计数。 0=[nothing], 1=[minus], 2=[plus]. 递归对于这种赋值很有用。 重复变化。你可以这样做:使用一个循环遍历所有 8 个可能的位置,然后在另一个(内部)循环的帮助下,插入一个“+”、一个“-”或一个空格。然后遍历整个字符串,解析它并执行计算。 不是有9个可能的位置,包括第一个标志吗? 您可以添加+ 或不添加任何内容,这不会影响数字,但- 肯定会。 【参考方案1】:

基本思路:生成+-运算符的所有可能变体(包括缺少运算符的情况),然后解析字符串并求和。 p>

方法:组合起来,很容易证明我们可以通过将运算符(或不存在)与基数为 3 的数字相关联来做到这一点。所以我们可以遍历每一个 8 位的三进制数,但我们不会打印 0、1 和 2,而是在字符串中的下一个数字之前附加一个“+”、一个“-”或什么都不加。

请注意,我们实际上并不需要一个字符串;也可以直接使用数字和运算符等,即时计算结果。我只采用了基于字符串的方法,因为它解释简单,实现起来也很简单,另外,它给了我们一些视觉反馈,有助于理解解决方案。

现在我们已经构建了字符串,我们可以解析它了;最简单的解决方案是为此目的使用 C 标准库函数strtol(),它将考虑符号并返回一个有符号整数。因此,我们可以在一个简单的循环中对所有有符号整数求和,就完成了。

代码:

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>

int main()

    const char *ops = " +-";

    // 3 ^ 8 = 6561
    for (int i = 0; i < 6561; i++) 
        // first, generate the line
        int k = i;
        std::string line = "1";
        for (int j = 0; j < 8; j++) 
            if (k % 3)
                line += ops[k % 3];

            k /= 3;
            line += (char)('2' + j);
        

        // now parse it
        int result = 0;
        const char *s = line.c_str();
        char *p;

        while (*s) 
            int num = strtol(s, &p, 10);
            result += num;
            s = p;
        

        // output
        std::cout << line << " = " << result << (result == 120 ? " MATCH" : "") << std::endl;
    

    return 0;

结果:

h2co3-macbook:~ h2co3$ ./quirk | grep MATCH
12-3-45+67+89 = 120 MATCH
1+2-34-5+67+89 = 120 MATCH
12-3+4+5+6+7+89 = 120 MATCH
1-23+4+56-7+89 = 120 MATCH
1+2+34-5+6-7+89 = 120 MATCH
123+4+5-6-7-8+9 = 120 MATCH
1+2-3+45+6+78-9 = 120 MATCH
12-3+45+67+8-9 = 120 MATCH
123+4-5+6-7+8-9 = 120 MATCH
123-4+5+6+7-8-9 = 120 MATCH
h2co3-macbook:~ h2co3$ 

【讨论】:

很有趣,但其中有些部分我不明白,尤其是最后的 while 循环。你能解释一下它是如何工作的吗? 另外,如果您使用的数字不只是 1-9,例如 90210=50 之类的,我认为这不起作用。 @user3221115 嗯,什么?为什么不起作用/不会起作用? @user3221115 关于循环:如果您阅读strtol() 的文档,您会发现它的第二个参数将被设置为指向解析后的数字后的第一个字符。因此,通过编写s = p;,您可以遍历数字的文本表示(以及前面的符号),将它们转换为有符号(正负)整数并将它们相加以获得最终结果。【参考方案2】:

下面的bool advance(string&amp; s) 函数将为您提供'+''-'' ' 的任意长度字符串的所有组合,除了一个之外,如果没有更多可用字符串,则返回false

char advance(char c)

    switch (c)
    
        case ' ': return '+';
        case '+': return '-';
        default: case '-': return ' ';
    


bool advance(string& s)

    for (int i = 0; i < s.size(); ++i)
        if ((s[i] = advance(s[i])) != ' ')
            return true;

    return false;

你必须先给它一个只包含所需长度的空格的字符串,然后重复“推进”它。用法:

string s = "    ";
while (advance(s))
    cout << '"' << s << '"' << endl;

上面的代码会打印出来

"+   "
"-   "
" +  "
"++  "
"-+  "
" -  "
.
.
.
" ---"
"+---"
"----"

请注意,只有 4 个空格的“第一个”组合不会打印出来。

您可以将这些组合与您的 lhs 交错,跳过空格,以生成表达式。

【讨论】:

【参考方案3】:

另一种非常相似的方法,在普通 C 中 好的,如果你真的想要这样的话,在 C++ 中;) 并且更易于配置

同样的以 3 为底的数字技巧用于枚举 void、+ 和 - 运算符的组合。

字符串被处理为加在一起的正值或负值列表。

另一个贡献非常紧凑和优雅,但使用了一些 C 技巧来缩短代码。 希望这个更详细一点,尽管没有那么漂亮。

#include <iostream>
#include <string>
using namespace std;

#include <string.h>
#include <math.h>

void solver (const char * str, int result)

    int op_max = pow(3, strlen(str)); // number of operator permutations

    // loop through all possible operator combinations
    for (int o = 0 ; o != op_max ; o++)
    
        int res  = 0; // computed operation result
        int sign = 1; // sign of the current value

        int val = str[0]-'0'; // read 1st digit
        string litteral;      // litteral display of the current operation

        // parse remaining digits
        int op;
        for (unsigned i=1, op=o ; i != strlen (str) ; i++, op/=3)
        
            // get current digit
            int c = str[i]-'0';

            // get current operator
            int oper = op % 3;

            // apply operator
            if (oper == 0) val = 10*val + c;
            else
            
                // add previous value
                litteral += sign*val;
                res += sign*val;

                // store next sign
                sign = oper == 1 ? 1 : -1;

                // start a new value
                val = c;
            
           

        // add last value
        litteral += sign*val;
        res += sign*val;

        // check result
        if (res == result)
        
            cout << litteral << " = " << result << endl;
        
    


int main(void)

    solver ("123456789", 120);

注意:我出于懒惰而使用了std::strings,尽管它们的速度非常慢。

【讨论】:

纯 C 很好,我更喜欢它,但问题是关于 C++。 @H2CO3 嗯,从技术上讲,C 是 C++ 的子部分 :)。无论如何,你的解决方案比我的更优雅。 NO NO NO NO. C 不是 C++ 的子集。从来没有。有很多不兼容的地方。 只是我的小玩笑:)。您可以轻松地将 C 和 C++ 链接在一起,仅此而已。 是的,没错。但它不会使 C 成为 C++ 的子集。请不要做出错误的断言,尤其是当初学者也在听的时候,因为他们会很困惑……:-(

以上是关于在这种情况下,我将如何遍历所有各种可能性?的主要内容,如果未能解决你的问题,请参考以下文章

在这种复杂情况下,如何解决 Django 缺少组合键的问题?

有没有办法在没有嵌套循环的情况下遍历数字列表的所有组合?

如何在不递归的情况下找到所有可能的字谜?

如何遍历所有子窗体 MS Access VBA

在这种情况下如何获取两个字符之间的字符串?

powershell 这将循环遍历SharePoint列表中的所有项并更新该值。在这种情况下,它将标题从小写更改为大写