【问题标题】:Linq Arithemetic Operator combinationsLinq 算术运算符组合
【发布时间】:2009-10-18 09:27:45
【问题描述】:

当试图解决以下任务时:

使用算术运算符(+、-、*、/)重新排列四个五以等于数字 1 到 10。

示例:5/5+5-5 =1 ,5/5+5/5=2

我尝试在 C# 中不使用 Linq(我不知道如何进一步处理)

public void GetDetails()
{

   char[] sym = new char[] { '+', '-', '/', '*' };

   int[] AOf5 = new int[] { 5, 5, 5, 5 };


for (int i = 0; i <4; i++)
 {
    for (int j = 0; j <4; j++)
     {
       for (int k = 0; k <4; k++)
          {
             for (int l = 0; l < 4; l++)
              {

                int result1 = AOf5[0] + sym[i] + AOf5[1] + sym[j] +
                AOf5[2] + sym[k] + AOf5[3];

               int result2 = AOf5[0] + sym[i] + AOf5[1] + sym[j] +
               AOf5[2] + sym[l] + AOf5[3];

              int result3 = AOf5[0] + sym[i] + AOf5[1] +
              sym[k] + AOf5[2] + sym[l] + AOf5[3];
              ....
              ....

              }  

         }

      }
  }

}

如果没有 linq 和使用 linq,我无法完成它。期待您的帮助。

【问题讨论】:

  • 所以基本上,您正在遍历所有可能的排列并挑选出那些给出正确答案的排列并构建代表算术的字符串?愿意分享一下您是如何使用 LINQ 实现的吗?
  • 只是出于好奇:有没有人设法获得生成 3..8 的方法?我使用 PowerShell 进行的小测试只产生了 1、2、9 和 10 的结果。
  • 可以使用括号吗? (5*5+5)/5 == 6, (5+5+5)/5 == 3
  • 我不知道关键字“重新排列”的真正含义,但我认为我们应该考虑括号和操作顺序。
  • 没有8的解决方案吧?我很确定我进行了详尽的搜索,但从未出现 8 个。

标签: c# linq linq-to-objects math combinations


【解决方案1】:

应用从左到右(无优先级),我可以得到:

1: ((5+5)-5)/5
2:
3: ((5+5)+5)/5
4: ((5*5)-5)/5
5: ((5-5)*5)+5
6: ((5*5)+5)/5
7: ((5+5)/5)+5
8:
9:
10: ((5+5)+5)-5

有了(编辑:哎呀——“没有 div”的东西是不必要的):

    var operators = new[] {
          new { Name = "+", Func = (Func<decimal,decimal,decimal>)((x,y)=>x+y) },
          new { Name = "-", Func = (Func<decimal,decimal,decimal>)((x,y)=>x-y) },
          new { Name = "/", Func = (Func<decimal,decimal,decimal>)((x,y)=>x/y) },
          new { Name = "*", Func = (Func<decimal,decimal,decimal>)((x,y)=>x*y) }
      };
    var options = from i in Enumerable.Range(1, 10)
                  select new {i, op=(
                    from op1 in operators
                    let v1 = op1.Func(5,5)
                    from op2 in operators
                    let v2 = op2.Func(v1, 5)
                    from op3 in operators
                    let v3 = op3.Func(v2,5)
                    where v3 == i
                    select "((5" + op1.Name + "5)" + op2.Name + "5)"
                       + op3.Name + "5").FirstOrDefault()};
    foreach (var opt in options)
    {
        Console.WriteLine(opt.i + ": " + opt.op);
    }

【讨论】:

  • @Marc:你的概念是要走的路,虽然我很确定,有些操作被遗漏了:“((5 + 5)/ 5)* 5 = 10”,“( (5/5)+5)-5 = 1" 等等。你的 Perpahs 是 > 1 和
  • 这不包括:9 = (5+5) - (5/5);和 2 = (5/5) + (5/5)。
  • @Alex: (5+5)-(5/5) 正在使用优先规则。 Marc 已经指定他的算法是从左到右的,没有优先级。
  • @o.k.w:注意“FirstOrDefault()”。他选择了第一个符合所需结果的人。另外,作者清楚地表明他想要优先规则,正如他在第二个示例“5/5+5/5=2”中提供的那样,这将使“1.2”没有优先级。
  • @Alex:是的,答案并不能完全满足这个问题。只是陈述马克的“免责声明”。 :P
【解决方案2】:

我以一种原始的方式完成了它,我不确定答案是否正确。不过,在电子表格中更容易完成。基本上我修改了linqfying的代码来生成代码。

这里是:

public static void GetDetails()
{
    int ctr = 0;
    char[] sym = new char[] { '+', '-', '/', '*' };
    string num = "5";
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            for (int k = 0; k < 4; k++)
            {
                for (int l = 0; l < 4; l++)
                {
                    ctr++;
                    string str = num + sym[i] + num + sym[j] + num + sym[k] + num;
                    Console.WriteLine("res = " + str + "; ");
                    Console.WriteLine("if(res>=1 && res<=10)");
                    Console.WriteLine("Console.WriteLine(\"" + str + "\");");
                }

            }

        }
    }
    //Console.WriteLine("Total:" + ctr.ToString());
}

它会生成 256 组操作,我将它们输出到一个文本文件中,然后将它们复制并粘贴到一个新方法中:

public static void runit()
{
    float res = 0;
    res = 5+5+5+5;
    if (res >= 1 && res <= 10)
        Console.WriteLine("5+5+5+5");
    res = 5+5+5+5;
    if (res >= 1 && res <= 10)
        Console.WriteLine("5+5+5+5");
    res = 5+5+5+5;
    if (res >= 1 && res <= 10)
        Console.WriteLine("5+5+5+5");
    //......
    //......
    //......
    //......
    res = 5*5*5*5;
    if (res >= 1 && res <= 10)
        Console.WriteLine("5*5*5*5");

}

再次运行它,我得到了 76 个非唯一操作,这些操作介于 1 到 10 之间。 这里有 19 个独特的(仅限从左到右的操作):

5*5/5/5
5*5/5+5
5/5*5/5
5/5*5+5
5/5/5+5
5/5+5/5
5/5+5-5
5/5-5+5
5+5*5/5
5+5/5*5
5+5/5/5
5+5/5-5
5+5+5-5
5+5-5/5
5+5-5+5
5-5/5/5
5-5/5+5
5-5+5/5
5-5+5+5

我相信有人可以提出更有创意的东西:P

添加:

在匹配 Marc 的答案后,我意识到最初的循环并未涵盖所有排列,我的答案不会是正确的。但既然我已经花了很长时间,我就让它留下来。 :P

【讨论】:

  • 感谢您的努力。非常感谢我的朋友
  • @linqfying:没问题,拿 Marc 的那个,稍微调整一下,你应该会得到它。但是,如果您想完全避免使用 LINQ,则必须进行一些调整。
  • 是的,没有 Linq,我正在锻炼。一旦完成,我将发布它。现在我正在解决另一个有趣的问题。感谢您的回复 :)
【解决方案3】:

Marc 的解决方案中唯一缺少的情况是具有操作优先级的情况,例如:5/5+5/5。我将它们添加到一个联合中,这是正确的查询。
马克,如果你用下面的代码更新你的答案(如果你认为它是正确的)我会删除这个答案:

var operators = new[] {
              new { Name = "+", Func = (Func<decimal,decimal,decimal>)((x,y)=>x+y) },
              new { Name = "-", Func = (Func<decimal,decimal,decimal>)((x,y)=>x-y) },
              new { Name = "/", Func = (Func<decimal,decimal,decimal>)((x,y)=>x/y) },
              new { Name = "*", Func = (Func<decimal,decimal,decimal>)((x,y)=>x*y) }
          };

var options = from i in Enumerable.Range(1, 10)
              select new
              {
                  i,
                  op = (
                      from op1 in operators
                      let v1 = op1.Func(5, 5)
                      from op2 in operators
                      let v2 = op2.Func(v1, 5)
                      from op3 in operators
                      let v3 = op3.Func(v2, 5)
                      where v3 == i
                      select "((5" + op1.Name + "5)" + op2.Name + "5)"
                         + op3.Name + "5")
                      .Union(
             //calculate 2 operations (the left and the right one),  
             //then operate them together.
                        from op1 in operators
                        let v1 = op1.Func(5, 5)
                        from op2 in operators
                        let v2 = op2.Func(5, 5)
                        from op3 in operators
                        let v3 = (op3.Name == "/" && v2 == 0) ? null : (int?)op3.Func(v1, v2)
                        where v3 == i
                        select "(5" + op1.Name + "5)" + op2.Name + "(5"
                             + op3.Name + "5)"
                      ).FirstOrDefault()
              };

foreach (var opt in options)
        {
            Console.WriteLine(opt.i + ": " + opt.op);
        }

编辑
关于let v3 = (op3.Name == "/" &amp;&amp; v2 == 0) ? null : (int?)op3.Func(v1, v2) 的几句话:这是避免除以0 的一种非常有效的方法(这种情况可能会发生,因为您可以除以(5-5))。

我很确定你可以用更好的方式过滤它,但我留下它是为了强调这个问题可能会发生。

【讨论】:

    【解决方案4】:

    假设您不想使用 LINQ,这里有一种实现方法。也就是说,它非常未优化,我建议在它之上使用更短的 LINQ 实现。 (参见:Marc Gravell's post。)

    using System;
    using System.Collections.Generic;
    
    namespace MathIterator
    {
      class Program
      {
        static readonly int[] _inputs = new int[] { 5, 5, 5, 5 };
        static readonly char[] _operations = new char[] { '+', '-', '*', '/' };
        static Dictionary<int, List<string>> _calculations = new Dictionary<int, List<string>>();
    
        static void Main(string[] args)
        {
          StartPermutation();
          PrintResults();
        }
    
        static void StartPermutation()
        {
          if (_inputs.Length > 0)
            Permute(1 /*index*/, _inputs[0], _inputs[0].ToString());    
        }
    
        static void Permute(int index, int result, string computation)
        {
          if (index == _inputs.Length)
          {
            if (!_calculations.ContainsKey(result))
            {
              _calculations[result] = new List<string>();
            }
    
            _calculations[result].Add(computation);
          }
          else
          {
            foreach (char operation in _operations)
            {
              string nextComputation = String.Format("({0} {1} {2})",computation, operation, _inputs[index]);
              int nextResult = result;
    
              switch (operation)
              {
                case '+':
                  nextResult += _inputs[index];
                  break;
                case '-':
                  nextResult -= _inputs[index];
                  break;
                case '*':
                  nextResult *= _inputs[index];
                  break;
                case '/':
                  nextResult /= _inputs[index];
                  break;
              }
    
              Permute(
                index + 1,
                nextResult,
                nextComputation);
            }
          }
        }
    
        static void PrintResults()
        {
          for (int i = 1; i <= 10; ++i)
          {
            if (_calculations.ContainsKey(i))
            {
              Console.WriteLine("Found {0} entries for key {1}", _calculations[i].Count, i);
    
              foreach (string calculation in _calculations[i])
              {
                Console.WriteLine(i + " = " + calculation);
              }
            }
            else
            {
              Console.WriteLine("No entry for key: " + i);
            }
          }
        }
      }
    }
    

    这是另一个实现。这一个遵循优先顺序。同样,我不建议这样解决。考虑到宽破折号(将它们与减号区分开来),现在更是如此,但它似乎确实有效。

    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    
    namespace MathIterator
    {
      class Program
      {
        static readonly int[] _inputs = new[] { 5, 5, 5, 5 };
        //HUGE hack, the '–' is a wide dash NOT a hyphen.
        static readonly char[][] _operationLevels = new[] { new[] { '*', '/' }, new[] { '+', '–' } };
        static List<string> _calculations = new List<string>();
        static Dictionary<int, List<string>> _results = new Dictionary<int, List<string>>();
    
        static void Main(string[] args)
        {
          StartPermutation();
          StartEvaluateCalculations();
          PrintResults();
        }
    
        static void StartPermutation()
        {
          if (_inputs.Length > 0)
            Permute(1 /*index*/, _inputs[0].ToString());    
        }
    
        static void Permute(int index, string computation)
        {
          if (index == _inputs.Length)
          {
            _calculations.Add(computation);
          }
          else
          {
            foreach (char[] operationLevel in _operationLevels)
            {
              foreach (char operation in operationLevel)
              {
                string nextComputation = String.Format("{0} {1} {2}", computation, operation, _inputs[index]);
                Permute(
                  index + 1,
                  nextComputation);
              }
            }
          }
        }
    
        static void StartEvaluateCalculations()
        {
          foreach (string calculation in _calculations)
          {
            int? result = EvaluateCalculation(calculation);
    
            if (result != null)
            {
              int intResult = (int) result;
    
              if (!_results.ContainsKey(intResult))
              {
                _results[intResult] = new List<string>();
              }
    
              _results[intResult].Add(calculation);            
            }
          }
        }
    
        static int? EvaluateCalculation(string calculation)
        {
          foreach (char[] operationLevel in _operationLevels)
          {
            string[] results = calculation.Split(operationLevel, 2);
    
            if (results.Length == 2)
            {
              int hitIndex = results[0].Length;
    
              Regex firstDigit = new Regex(@"^ -?\d+");
              Regex lastDigit = new Regex(@"-?\d+ $");
    
              string firstMatch = lastDigit.Match(results[0]).Value;
              int arg1 = int.Parse(firstMatch);
    
              string lastMatch = firstDigit.Match(results[1]).Value; 
              int arg2 = int.Parse(lastMatch);
    
              int result = 0;
    
              switch (calculation[hitIndex])
              {
                case '+':
                  result = arg1 + arg2;
                  break;
                //HUGE hack, the '–' is a wide dash NOT a hyphen.
                case '–':
                  result = arg1 - arg2;
                  break;
                case '*':
                  result = arg1 * arg2;
                  break;
                case '/':
                  if ((arg2 != 0) && ((arg1 % arg2) == 0))
                  {
                    result = arg1 / arg2;
                    break;
                  }
                  else
                  {
                    return null;
                  }
              }
    
              string prePiece = calculation.Remove(hitIndex - 1 - arg1.ToString().Length);
              string postPiece = calculation.Substring(hitIndex + 1 + lastMatch.ToLower().Length);
    
              string nextCalculation = prePiece + result + postPiece;
              return EvaluateCalculation(nextCalculation);
            }
          }
    
          return int.Parse(calculation);
        }
    
        static void PrintResults()
        {
          for (int i = 1; i <= 10; ++i)
          {
            if (_results.ContainsKey(i))
            {
              Console.WriteLine("Found {0} entries for key {1}", _results[i].Count, i);
    
              foreach (string calculation in _results[i])
              {
                Console.WriteLine(i + " = " + calculation);
              }
            }
            else
            {
              Console.WriteLine("No entry for key: " + i);
            }
          }
        }
      }
    }
    

    【讨论】:

    • @Rob:我运行了你的代码,21 个独特的结果。看起来很有希望!虽然仍然缺乏优先规则。无论如何都做得很好。
    • 哇,这肯定比我在这里摆弄的大小要好:-) ... $ops="+","-","*","/";1..10|%{$res=$_; $ops|%{$o1=$_; $ops|%{$o2=$_; $ops|%{$o3=$_; "((5 $o1 5) $o2 5) $o3 5"}}}|?{(iex $_) -eq $res}|%{"$_ = $res"}}
    【解决方案5】:

    这是一个完全基于 LINQ(方法语法)和后期评估的解决方案,它处理所有排列(不仅是从左到右):

    static void Main()
    {
        var solution = PermuteLength(4)
            .Where(p => Decimal.Floor(p.Value) == p.Value)
            .Where(p => p.Value <= 10 && p.Value >= 0)
            .OrderBy(p => p.Value);
    
        foreach (var p in solution)
        {
            Console.WriteLine(p.Formula + " = " + p.Value);
        }
    }
    
    public static Operator[] Operators = new[]
        {
            new Operator {Format = "({0} + {1})", Function = (x, y) => x + y},
            new Operator {Format = "({0} - {1})", Function = (x, y) => x - y},
            new Operator {Format = "({1} - {0})", Function = (x, y) => y - x},
            new Operator {Format = "({0} * {1})", Function = (x, y) => x * y},
            new Operator {Format = "({0} / {1})", Function = (x, y) => y == 0 ? 0 : x / y},
            new Operator {Format = "({1} / {0})", Function = (x, y) => x == 0 ? 0 : y / x},
        };
    
    public static IEnumerable<Permutation> BasePermutation = new[] { new Permutation {Formula = "5", Value = 5m} };
    
    private static IEnumerable<Permutation> PermuteLength(int length)
    {
        if (length <= 1)
            return BasePermutation;
    
        var result = Enumerable.Empty<Permutation>();
    
        for (int i = 1; i <= length / 2; i++)
            result = result.Concat(Permute(PermuteLength(i), PermuteLength(length - i)));
    
        return result;
    }
    
    private static IEnumerable<Permutation> Permute(IEnumerable<Permutation> left, IEnumerable<Permutation> right)
    {
        IEnumerable<IEnumerable<IEnumerable<Permutation>>> product = left.Select(l => right.Select(r => ApplyOperators(l, r)));
    
        var aggregate =
            product.Aggregate(Enumerable.Empty<IEnumerable<Permutation>>(), (result, item) => result.Concat(item)).
                Aggregate(Enumerable.Empty<Permutation>(), (result, item) => result.Concat(item));
    
        return aggregate;
    }
    
    private static IEnumerable<Permutation> ApplyOperators(Permutation left, Permutation right)
    {
        return Operators.Select(o => new Permutation
        {
            Formula = string.Format(o.Format, left.Formula, right.Formula),
            Value = o.Function(left.Value, right.Value)
        });
    }
    
    public struct Permutation
    {
        public string Formula;
        public decimal Value;
    }
    
    public struct Operator
    {
        public string Format;
        public Func<decimal, decimal, decimal> Function;
    }
    

    已知问题: 有些解决方案是重复的, 不能很好地处理除零,所以一些错误的答案(我假设任何东西除以零 = 0)

    编辑: 部分结果:

    ((5 / 5) / (5 / 5)) = 1

    ((5 / 5) + (5 / 5)) = 2

    ((5 + (5 + 5)) / 5) = 3

    (5 - ((5 + 5) / 5)) = 3

    (((5 * 5) - 5) / 5) = 4

    (5 + (5 * (5 - 5))) = 5

    (5 - (5 * (5 - 5))) = 5

    (5 + ((5 - 5) / 5)) = 5

    (5 - ((5 - 5) / 5)) = 5

    【讨论】:

      猜你喜欢
      • 2022-01-07
      • 2012-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-23
      • 2019-03-30
      相关资源
      最近更新 更多