用C#表达式树优雅的计算24点

思路:一共4个数字,共需要3个运算符,可以构造一个二叉树,没有子节点的节点的为值,有叶子节点的为运算符

例如数字{1, 2, 3, 4},其中一种解的二叉树形式如下所示:

因此可以遍历所有二叉树可能的形式,4个数的全排列,从4种运算符中挑选3种运算符(运算符可以重复)

核心步骤1:需要遍历所有二叉树的可能,参考Eric Lippert的方法

class Node
{
    public Node Left { get; private set; }
    public Node Right { get; private set; }
    public Node(Node left, Node right)
    {
        this.Left = left;
        this.Right = right;
    }
}

static IEnumerable<Node> AllBinaryTrees(int size)
{
    if (size == 0)
        return new Node[] { null };
    return from i in Enumerable.Range(0, size)
            from left in AllBinaryTrees(i)
            from right in AllBinaryTrees(size - 1 - i)
            select new Node(left, right);
}

核心步骤2:对于任意一个二叉树,构造表达式树

static Expression Build(Node node, List<double> numbers, List<Func<Expression, Expression, BinaryExpression>> operators)
{
    var iNum = 0;
    var iOprt = 0;

    Func<Node, Expression> f = null;
    f = n =>
    {
        Expression exp;
        if (n == null)
            exp = Expression.Constant(numbers[iNum++]);
        else
        {
            var left = f(n.Left);
            var right = f(n.Right);
            exp = operators[iOprt++](left, right);
        }
        return exp;
    };
    return f(node);
}

核心步骤3:遍历4个数字的全排列,全排列参考这里

static IEnumerable<List<T>> FullPermute<T>(List<T> elements)
{
    if (elements.Count == 1)
        return EnumerableOfOneElement(elements);

    IEnumerable<List<T>> result = null;
    foreach (T first in elements)
    {
        List<T> remaining = elements.ToArray().ToList();
        remaining.Remove(first);
        IEnumerable<List<T>> fullPermuteOfRemaining = FullPermute(remaining);

        foreach (List<T> permute in fullPermuteOfRemaining)
        {
            var arr = new List<T> { first };
            arr.AddRange(permute);

            var seq = EnumerableOfOneElement(arr);
            if (result == null)
                result = seq;
            else
                result = result.Union(seq);
        }
    }
    return result;
}

static IEnumerable<T> EnumerableOfOneElement<T>(T element)
{
    yield return element;
}

例如有四个数字{1, 2, 3, 4},它的全排列如下:

1, 2, 3, 4
1, 2, 4, 3
1, 3, 2, 4
1, 3, 4, 2
1, 4, 2, 3
1, 4, 3, 2
2, 1, 3, 4
2, 1, 4, 3
2, 3, 1, 4
2, 3, 4, 1
2, 4, 1, 3
2, 4, 3, 1
3, 1, 2, 4
3, 1, 4, 2
3, 2, 1, 4
3, 2, 4, 1
3, 4, 1, 2
3, 4, 2, 1
4, 1, 2, 3
4, 1, 3, 2
4, 2, 1, 3
4, 2, 3, 1
4, 3, 1, 2
4, 3, 2, 1

核心步骤4:从4种运算符中挑选3个运算符

static IEnumerable<IEnumerable<Func<Expression, Expression, BinaryExpression>>> OperatorPermute(List<Func<Expression, Expression, BinaryExpression>> operators)
{
    return from operator1 in operators
            from operator2 in operators
            from operator3 in operators
            select new[] { operator1, operator2, operator3 };
}

最后是Main函数:

static void Main(string[] args)
{
    List<double> numbers = new List<double> { 1, 2, 3, 4 };
    var operators = new List<Func<Expression, Expression, BinaryExpression>> {
        Expression.Add,Expression.Subtract,Expression.Multiply,Expression.Divide
    };

    foreach (var operatorCombination in OperatorPermute(operators))
    {
        foreach (Node node in AllBinaryTrees(3))
        {
            foreach (List<double> permuteOfNumbers in FullPermute(numbers))
            {
                Expression expression = Build(node, permuteOfNumbers, operatorCombination.ToList());
                Func<double> compiled = Expression.Lambda<Func<double>>(expression).Compile();
                try
                {
                    var value = compiled();
                    if (Math.Abs(value - 24) < 0.01)
                        Console.WriteLine("{0} = {1}", expression, value);
                }
                catch (DivideByZeroException) { }
            }
        }
    }
    Console.Read();
}

计算结果:

(4 * (1 + (2 + 3))) = 24
(4 * (1 + (3 + 2))) = 24
(4 * (2 + (1 + 3))) = 24
(4 * (2 + (3 + 1))) = 24
(4 * (3 + (1 + 2))) = 24
(4 * (3 + (2 + 1))) = 24
(4 * ((1 + 2) + 3)) = 24
(4 * ((1 + 3) + 2)) = 24
(4 * ((2 + 1) + 3)) = 24
(4 * ((2 + 3) + 1)) = 24
(4 * ((3 + 1) + 2)) = 24
(4 * ((3 + 2) + 1)) = 24
((1 + 3) * (2 + 4)) = 24
((1 + 3) * (4 + 2)) = 24
((2 + 4) * (1 + 3)) = 24
((2 + 4) * (3 + 1)) = 24
((3 + 1) * (2 + 4)) = 24
((3 + 1) * (4 + 2)) = 24
((4 + 2) * (1 + 3)) = 24
((4 + 2) * (3 + 1)) = 24
((1 + (2 + 3)) * 4) = 24
((1 + (3 + 2)) * 4) = 24
((2 + (1 + 3)) * 4) = 24
((2 + (3 + 1)) * 4) = 24
((3 + (1 + 2)) * 4) = 24
((3 + (2 + 1)) * 4) = 24
(((1 + 2) + 3) * 4) = 24
(((1 + 3) + 2) * 4) = 24
(((2 + 1) + 3) * 4) = 24
(((2 + 3) + 1) * 4) = 24
(((3 + 1) + 2) * 4) = 24
(((3 + 2) + 1) * 4) = 24
(1 * (2 * (3 * 4))) = 24
(1 * (2 * (4 * 3))) = 24
(1 * (3 * (2 * 4))) = 24
(1 * (3 * (4 * 2))) = 24
(1 * (4 * (2 * 3))) = 24
(1 * (4 * (3 * 2))) = 24
(2 * (1 * (3 * 4))) = 24
(2 * (1 * (4 * 3))) = 24
(2 * (3 * (1 * 4))) = 24
(2 * (3 * (4 * 1))) = 24
(2 * (4 * (1 * 3))) = 24
(2 * (4 * (3 * 1))) = 24
(3 * (1 * (2 * 4))) = 24
(3 * (1 * (4 * 2))) = 24
(3 * (2 * (1 * 4))) = 24
(3 * (2 * (4 * 1))) = 24
(3 * (4 * (1 * 2))) = 24
(3 * (4 * (2 * 1))) = 24
(4 * (1 * (2 * 3))) = 24
(4 * (1 * (3 * 2))) = 24
(4 * (2 * (1 * 3))) = 24
(4 * (2 * (3 * 1))) = 24
(4 * (3 * (1 * 2))) = 24
(4 * (3 * (2 * 1))) = 24
(1 * ((2 * 3) * 4)) = 24
(1 * ((2 * 4) * 3)) = 24
(1 * ((3 * 2) * 4)) = 24
(1 * ((3 * 4) * 2)) = 24
(1 * ((4 * 2) * 3)) = 24
(1 * ((4 * 3) * 2)) = 24
(2 * ((1 * 3) * 4)) = 24
(2 * ((1 * 4) * 3)) = 24
(2 * ((3 * 1) * 4)) = 24
(2 * ((3 * 4) * 1)) = 24
(2 * ((4 * 1) * 3)) = 24
(2 * ((4 * 3) * 1)) = 24
(3 * ((1 * 2) * 4)) = 24
(3 * ((1 * 4) * 2)) = 24
(3 * ((2 * 1) * 4)) = 24
(3 * ((2 * 4) * 1)) = 24
(3 * ((4 * 1) * 2)) = 24
(3 * ((4 * 2) * 1)) = 24
(4 * ((1 * 2) * 3)) = 24
(4 * ((1 * 3) * 2)) = 24
(4 * ((2 * 1) * 3)) = 24
(4 * ((2 * 3) * 1)) = 24
(4 * ((3 * 1) * 2)) = 24
(4 * ((3 * 2) * 1)) = 24
((1 * 2) * (3 * 4)) = 24
((1 * 2) * (4 * 3)) = 24
((1 * 3) * (2 * 4)) = 24
((1 * 3) * (4 * 2)) = 24
((1 * 4) * (2 * 3)) = 24
((1 * 4) * (3 * 2)) = 24
((2 * 1) * (3 * 4)) = 24
((2 * 1) * (4 * 3)) = 24
((2 * 3) * (1 * 4)) = 24
((2 * 3) * (4 * 1)) = 24
((2 * 4) * (1 * 3)) = 24
((2 * 4) * (3 * 1)) = 24
((3 * 1) * (2 * 4)) = 24
((3 * 1) * (4 * 2)) = 24
((3 * 2) * (1 * 4)) = 24
((3 * 2) * (4 * 1)) = 24
((3 * 4) * (1 * 2)) = 24
((3 * 4) * (2 * 1)) = 24
((4 * 1) * (2 * 3)) = 24
((4 * 1) * (3 * 2)) = 24
((4 * 2) * (1 * 3)) = 24
((4 * 2) * (3 * 1)) = 24
((4 * 3) * (1 * 2)) = 24
((4 * 3) * (2 * 1)) = 24
((1 * (2 * 3)) * 4) = 24
((1 * (2 * 4)) * 3) = 24
((1 * (3 * 2)) * 4) = 24
((1 * (3 * 4)) * 2) = 24
((1 * (4 * 2)) * 3) = 24
((1 * (4 * 3)) * 2) = 24
((2 * (1 * 3)) * 4) = 24
((2 * (1 * 4)) * 3) = 24
((2 * (3 * 1)) * 4) = 24
((2 * (3 * 4)) * 1) = 24
((2 * (4 * 1)) * 3) = 24
((2 * (4 * 3)) * 1) = 24
((3 * (1 * 2)) * 4) = 24
((3 * (1 * 4)) * 2) = 24
((3 * (2 * 1)) * 4) = 24
((3 * (2 * 4)) * 1) = 24
((3 * (4 * 1)) * 2) = 24
((3 * (4 * 2)) * 1) = 24
((4 * (1 * 2)) * 3) = 24
((4 * (1 * 3)) * 2) = 24
((4 * (2 * 1)) * 3) = 24
((4 * (2 * 3)) * 1) = 24
((4 * (3 * 1)) * 2) = 24
((4 * (3 * 2)) * 1) = 24
(((1 * 2) * 3) * 4) = 24
(((1 * 2) * 4) * 3) = 24
(((1 * 3) * 2) * 4) = 24
(((1 * 3) * 4) * 2) = 24
(((1 * 4) * 2) * 3) = 24
(((1 * 4) * 3) * 2) = 24
(((2 * 1) * 3) * 4) = 24
(((2 * 1) * 4) * 3) = 24
(((2 * 3) * 1) * 4) = 24
(((2 * 3) * 4) * 1) = 24
(((2 * 4) * 1) * 3) = 24
(((2 * 4) * 3) * 1) = 24
(((3 * 1) * 2) * 4) = 24
(((3 * 1) * 4) * 2) = 24
(((3 * 2) * 1) * 4) = 24
(((3 * 2) * 4) * 1) = 24
(((3 * 4) * 1) * 2) = 24
(((3 * 4) * 2) * 1) = 24
(((4 * 1) * 2) * 3) = 24
(((4 * 1) * 3) * 2) = 24
(((4 * 2) * 1) * 3) = 24
(((4 * 2) * 3) * 1) = 24
(((4 * 3) * 1) * 2) = 24
(((4 * 3) * 2) * 1) = 24
((2 * (3 * 4)) / 1) = 24
((2 * (4 * 3)) / 1) = 24
((3 * (2 * 4)) / 1) = 24
((3 * (4 * 2)) / 1) = 24
((4 * (2 * 3)) / 1) = 24
((4 * (3 * 2)) / 1) = 24
(((2 * 3) * 4) / 1) = 24
(((2 * 4) * 3) / 1) = 24
(((3 * 2) * 4) / 1) = 24
(((3 * 4) * 2) / 1) = 24
(((4 * 2) * 3) / 1) = 24
(((4 * 3) * 2) / 1) = 24
(2 * ((3 * 4) / 1)) = 24
(2 * ((4 * 3) / 1)) = 24
(3 * ((2 * 4) / 1)) = 24
(3 * ((4 * 2) / 1)) = 24
(4 * ((2 * 3) / 1)) = 24
(4 * ((3 * 2) / 1)) = 24
((2 * 3) * (4 / 1)) = 24
((2 * 4) * (3 / 1)) = 24
((3 * 2) * (4 / 1)) = 24
((3 * 4) * (2 / 1)) = 24
((4 * 2) * (3 / 1)) = 24
((4 * 3) * (2 / 1)) = 24
(((2 * 3) / 1) * 4) = 24
(((2 * 4) / 1) * 3) = 24
(((3 * 2) / 1) * 4) = 24
(((3 * 4) / 1) * 2) = 24
(((4 * 2) / 1) * 3) = 24
(((4 * 3) / 1) * 2) = 24
(2 / (1 / (3 * 4))) = 24
(2 / (1 / (4 * 3))) = 24
(3 / (1 / (2 * 4))) = 24
(3 / (1 / (4 * 2))) = 24
(4 / (1 / (2 * 3))) = 24
(4 / (1 / (3 * 2))) = 24
((2 * 3) / (1 / 4)) = 24
((2 * 4) / (1 / 3)) = 24
((3 * 2) / (1 / 4)) = 24
((3 * 4) / (1 / 2)) = 24
((4 * 2) / (1 / 3)) = 24
((4 * 3) / (1 / 2)) = 24
(2 * (3 * (4 / 1))) = 24
(2 * (4 * (3 / 1))) = 24
(3 * (2 * (4 / 1))) = 24
(3 * (4 * (2 / 1))) = 24
(4 * (2 * (3 / 1))) = 24
(4 * (3 * (2 / 1))) = 24
(2 * ((3 / 1) * 4)) = 24
(2 * ((4 / 1) * 3)) = 24
(3 * ((2 / 1) * 4)) = 24
(3 * ((4 / 1) * 2)) = 24
(4 * ((2 / 1) * 3)) = 24
(4 * ((3 / 1) * 2)) = 24
((2 / 1) * (3 * 4)) = 24
((2 / 1) * (4 * 3)) = 24
((3 / 1) * (2 * 4)) = 24
((3 / 1) * (4 * 2)) = 24
((4 / 1) * (2 * 3)) = 24
((4 / 1) * (3 * 2)) = 24
((2 * (3 / 1)) * 4) = 24
((2 * (4 / 1)) * 3) = 24
((3 * (2 / 1)) * 4) = 24
((3 * (4 / 1)) * 2) = 24
((4 * (2 / 1)) * 3) = 24
((4 * (3 / 1)) * 2) = 24
(((2 / 1) * 3) * 4) = 24
(((2 / 1) * 4) * 3) = 24
(((3 / 1) * 2) * 4) = 24
(((3 / 1) * 4) * 2) = 24
(((4 / 1) * 2) * 3) = 24
(((4 / 1) * 3) * 2) = 24
(2 * (3 / (1 / 4))) = 24
(2 * (4 / (1 / 3))) = 24
(3 * (2 / (1 / 4))) = 24
(3 * (4 / (1 / 2))) = 24
(4 * (2 / (1 / 3))) = 24
(4 * (3 / (1 / 2))) = 24
((2 / (1 / 3)) * 4) = 24
((2 / (1 / 4)) * 3) = 24
((3 / (1 / 2)) * 4) = 24
((3 / (1 / 4)) * 2) = 24
((4 / (1 / 2)) * 3) = 24
((4 / (1 / 3)) * 2) = 24
(2 / ((1 / 3) / 4)) = 24
(2 / ((1 / 4) / 3)) = 24
(3 / ((1 / 2) / 4)) = 24
(3 / ((1 / 4) / 2)) = 24
(4 / ((1 / 2) / 3)) = 24
(4 / ((1 / 3) / 2)) = 24

对于一些平时口算相对稍难的一些组合也是毫无压力,例如{1, 5, 5, 5}, {3, 3, 7, 7}, {3, 3, 8, 8},有兴趣的看官口算试试 :)

时间: 2024-10-14 06:54:17

用C#表达式树优雅的计算24点的相关文章

C#学习笔记(九):LINQ和表达式树

LINQ LINQ:语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据. 借助于LINQ技术,我们可以使用一种类似SQL的语法来查询任何形式的数据.目前为止LINQ所支持的数据源有SQL Server.Oracle.XML(标准通用标记语言下的一个应用)以及内存中的数据集合.开发人员也可以使用其提供的扩展框架添加更多的数据源,例如MySQL.Amaz

表达式计算--表达式树

目前只能算单位数,可计算括号与加减乘除. 代码如下: 1 #include <cstdio> 2 #include <cstring> 3 4 const int maxn = 1000; 5 int lch[maxn],rch[maxn]; 6 char op[maxn]; 7 int nc = 0,n; 8 char s[1000]; 9 10 int build_tree(char* s, int x , int y) 11 { 12 int c1 = -1,c2 = -1,

C#中的表达式树的浅解

表达式树可以说是Linq的核心之一,为什么是Linq的核心之一呢?因为表达式树使得c#不再是仅仅能编译成IL,我们可以通过c#生成一个表达式树,将结果作为一个中间格式,在将其转换成目标平台上的本机语言.比如SQL.我们常用的Linq to sql就是这样生成SQL的. 表达式树是.NET 3.5之后引入的,它是一个强大灵活的工具(比如用在LINQ中构造动态查询). 先来看看Expression类的API接口: namespace System.Linq.Expressions { // // 摘

什么是表达式树,它与表达式、委托有什么区别?

序言 首先,需要普及下基础知识: Expression我们称之为:表达式树, 而Func<>或者Action 称之为:匿名委托,Func与Action的区别是Func带返回值(至少一个参数),Action不带返回值(可以没有任何参数). 以上的关键词是在.net 3.5之后出现的,配合Linq中Lambda使用. 当然Expression还可以动态的进行构造它,而不使用Lambda表达式来定义. 什么是表达式树 它是一种数据结构体,用于存储需要计算.运算的一种结构.这种结构可以只是”存储“,而

16.C#初见Lambda表达式及表达式树(九章9.1-9.3)

在说明Lambda相关知识前,我们需要了解Lambda表达式常用于LINQ,那么我们来聊下LINQ. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑:如何过滤.如何排序以及如何将不同的数据源连接在一起,等等.执行委托只是LINQ的众多能力之一.为了富有效率地使用数据库和其他查询引擎,我们需要以一种不同的方式来表示管道中的各个操作.这种不同的方式就可以使用Lambda表达式来表现.下面分别使用委托(使用匿名函数)和Lambda表达式来作出同样的事情

前中后缀表达式以及表达式树

中缀表达式就是我们平时喜闻乐见的形式:二元运算符在中间,它的两个操作数在两侧: a + b * c + ( d * e + f ) * g 后缀和前缀表达式,顾名思义就是把运算符分别放在前面或者后面,注意没有括号,手工转换方法是按运算顺序添加括号,然后把相应的运算符置于相应的括号的前或后,如: ((a + ( b * c)) + (((d * e) + f) * g)) 放到前面有: +(+(a*(b c))*(+((*(d e))f)g)) 放到后面有: ((a(b c)*)+(((d e)*

[转] 利用表达式树构建委托改善反射性能

最近搞一个系统时由于在比较关键地方用到反射了,所以要关注了一下反射的性能问题.搜索一下,不难搜到老赵的这篇文章,下面是一些杂乱的笔记.(建议先看老赵的文章) .Net4.0反射性能改善 看老赵的文章,老赵得到的结果是这样的: 1 00:00:00.0125539 (Directly invoke) 2 00:00:04.5349626 (Reflection invoke) 3 00:00:00.0322555 (Dynamic executor) 而我把代码搞下来自己运行得到这样的结果: 1

C#中的Lambda表达式和表达式树

在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在不牺牲可读性的前提下,进一步简化了委托. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑,例如数据筛选,数据排序等等.通常这些操作都是用委托来表示.Lambda表达式是对LINQ数据操作的一种符合语言习惯的表示方式. Lambda表达式不仅可以用来创

表达式树,它与表达式、委托

表达式树,它与表达式.委托有什么区别? 序言 首先,需要普及下基础知识: Expression我们称之为:表达式树, 而Func<>或者Action 称之为:匿名委托,Func与Action的区别是Func带返回值(至少一个参数),Action不带返回值(可以没有任何参数). 以上的关键词是在.net 3.5之后出现的,配合Linq中Lambda使用. 当然Expression还可以动态的进行构造它,而不使用Lambda表达式来定义. 什么是表达式树 它是一种数据结构体,用于存储需要计算.运算