繁体   English   中英

从给定数字中获取所有可能的操作组合的算法

[英]Algorithm to get all the possible combinations of operations from a given numbers

我想写一个给定一组数字的函数,例如:2, 3

它返回带有 +、-、* 和 / 的所有操作组合。

这两个数字的结果是:

2+3   
2-3  
2*3  
2/3 

对于数字:2、3、4,它将是:

(2+3)+4   
(2+3)-4  
(2+3)*4  
(2+3)/4

(2-3)+4  
(2-3)-4  
(2-3)*4  
(2-3)/4  

...

2+(3+4)
2+(3*4)
2+(3-4)
2+(3/4)

...

3+(2+4)
3+(2*4)
3+(2-4)
3+(2/4)

...
and so on

运算符的顺序无关紧要,重点是从所有可能的操作组合中获得所有结果。

我将通过使用Reverse Polish Notation来解决这个问题,您可以将运算符和操作数附加到字符串,同时考虑一些简单的规则。

例如,表达式2 + (3 * 4)在逆波兰表示法中将是2 3 4 * + 另一方面, (2 + 3) * 4将是2 3 + 4 *

如果我们已经有一个部分表达式,我们可以添加一个操作数或一个运算符。

添加一个操作数总是可以完成的,并且会将堆栈的大小增加 1。另一方面,添加一个运算符会将堆栈的大小减少 1(移除最顶部的两个操作数并添加结果),因此可以仅当堆栈至少有两个条目时才执行。 最后,要形成有效的表达式,堆栈大小必须恰好为 1。

这激发了具有以下接口的递归函数:

getSubexpressions(remainingOperands, currentStackSize)

该函数返回一个子表达式列表,这些子表达式可以附加到堆栈大小为currentStackSize并使用操作数remainingOperands操作数的部分表达式。

这个递归函数的基本情况是当没有更多剩余的操作数并且堆栈大小为 1 时:

if remainingOperands = ∅ and currentStackSize = 1
    return { "" }

在这种情况下,我们只能将空字符串添加到表达式中。

在所有其他情况下,我们需要收集一组子表达式

subexpressions = { }   // initialize an empty set

如果我们可以添加一个运算符,我们可以简单地附加它:

if currentStackSize >= 2
    for each possible operator o
        subexpressions.add(o + getSubexpressions(remainingOperands, currentStackSize - 1))

符号o + getSubexpressions(remainingOperands, currentStackSize - 1)是将操作数o与调用getSubexpressions()返回的所有子表达式连接起来的简写。

我们就快到了。 最后剩下的位是添加潜在的操作数:

for each o in remainingOperands
    subexpressions.add(o + getSubexpressions(remainingOperands \ { o }, currentStackSize + 1))

符号remainingOperands \\ { o }代表集合差,即没有o的剩余操作数的集合。

就是这样。 在全:

getSubexpressions(remainingOperands, currentStackSize)
    if remainingOperands = ∅ and currentStackSize = 1
        return { "" }

    subexpressions = { }   // initialize an empty set

    if currentStackSize >= 2
        for each possible operator o
            subexpressions.add(o + getSubexpressions(remainingOperands, currentStackSize - 1))

    for each o in remainingOperands
        subexpressions.add(o + getSubexpressions(remainingOperands \ { o }, currentStackSize + 1))

    return subexpressions

这种递归调用通常会有重叠的子调用。 因此,您可以使用 memoization 来缓存中间结果,而不是一遍又一遍地重新计算它们。

这是在 C# 中没有记忆的概念验证实现。 特别是可以使用更合适的数据结构更有效地设计操作数管理:

static void Main(string[] args)
{
    foreach (var expr in GetSubexpressions(new List<string> { "1", "2", "3" }, 0, new StringBuilder()))
    {
        Console.WriteLine(expr);
    }
}

static char[] operators = { '+', '-', '*', '/' };

static IEnumerable<StringBuilder> GetSubexpressions(IList<string> remainingOperands, int currentStackSize, StringBuilder sb)
{
    if (remainingOperands.Count() == 0 && currentStackSize == 1)
    {
        yield return sb;
        yield break;
    }

    if(currentStackSize >= 2)
    {
        foreach (var o in operators)
        {
            sb.Append(o);
            foreach (var expr in GetSubexpressions(remainingOperands, currentStackSize - 1, sb))
                yield return expr;
            sb.Remove(sb.Length - 1, 1);
        }
    }

    for (int i = 0; i < remainingOperands.Count; ++i)
    {
        var operand = remainingOperands[i];
        remainingOperands.RemoveAt(i);
        sb.Append(operand);
        foreach (var expr in GetSubexpressions(remainingOperands, currentStackSize + 1, sb))
            yield return expr;
        sb.Remove(sb.Length - operand.Length, operand.Length);
        remainingOperands.Insert(i, operand);
    }
} 

该程序打印以下输出:

12+3+
12-3+
12*3+
12/3+
12+3-
12-3-
12*3-
12/3-
12+3*
12-3*
12*3*
12/3*
12+3/
12-3/
12*3/
12/3/
123++
123-+
123*+
123/+
123+-
123--
123*-
123/-
123+*
123-*
123**
123/*
123+/
123-/
123*/
123//
13+2+
13-2+
13*2+
13/2+
13+2-
13-2-
13*2-
13/2-
13+2*
13-2*
13*2*
13/2*
13+2/
13-2/
13*2/
13/2/
132++
132-+
132*+
132/+
132+-
132--
132*-
132/-
132+*
132-*
132**
132/*
132+/
132-/
132*/
132//
21+3+
21-3+
21*3+
21/3+
21+3-
21-3-
21*3-
21/3-
21+3*
21-3*
21*3*
21/3*
21+3/
21-3/
21*3/
21/3/
213++
213-+
213*+
213/+
213+-
213--
213*-
213/-
213+*
213-*
213**
213/*
213+/
213-/
213*/
213//
23+1+
23-1+
23*1+
23/1+
23+1-
23-1-
23*1-
23/1-
23+1*
23-1*
23*1*
23/1*
23+1/
23-1/
23*1/
23/1/
231++
231-+
231*+
231/+
231+-
231--
231*-
231/-
231+*
231-*
231**
231/*
231+/
231-/
231*/
231//
31+2+
31-2+
31*2+
31/2+
31+2-
31-2-
31*2-
31/2-
31+2*
31-2*
31*2*
31/2*
31+2/
31-2/
31*2/
31/2/
312++
312-+
312*+
312/+
312+-
312--
312*-
312/-
312+*
312-*
312**
312/*
312+/
312-/
312*/
312//
32+1+
32-1+
32*1+
32/1+
32+1-
32-1-
32*1-
32/1-
32+1*
32-1*
32*1*
32/1*
32+1/
32-1/
32*1/
32/1/
321++
321-+
321*+
321/+
321+-
321--
321*-
321/-
321+*
321-*
321**
321/*
321+/
321-/
321*/
321//

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM