惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
阮一峰的网络日志
阮一峰的网络日志
S
Secure Thoughts
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threat Research - Cisco Blogs
P
Privacy & Cybersecurity Law Blog
The Hacker News
The Hacker News
H
Heimdal Security Blog
W
WeLiveSecurity
L
LINUX DO - 热门话题
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
The Last Watchdog
The Last Watchdog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
D
DataBreaches.Net
I
Intezer
Webroot Blog
Webroot Blog
C
Cisco Blogs
AWS News Blog
AWS News Blog
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
V
Vulnerabilities – Threatpost
罗磊的独立博客
Google DeepMind News
Google DeepMind News
N
Netflix TechBlog - Medium
Schneier on Security
Schneier on Security
宝玉的分享
宝玉的分享
博客园 - 叶小钗
PCI Perspectives
PCI Perspectives
D
Docker
Scott Helme
Scott Helme
NISL@THU
NISL@THU
J
Java Code Geeks
B
Blog RSS Feed
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Exploit Database - CXSecurity.com
AI
AI
美团技术团队
Cloudbric
Cloudbric
月光博客
月光博客
P
Proofpoint News Feed
T
Tailwind CSS Blog
Google DeepMind News
Google DeepMind News
小众软件
小众软件
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Cloudflare Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

博客园 - Michael Peng

用马尔科夫模型做拼写检查 商业软件编程很无聊(转载) VS2010 Debugger bug 编程之初 编程之美 1.4买书问题常数时间空间解法 vc2010 std::tr1 bind库捉虫记 用vs2010编译kigg 3.0遇到的问题 在vs中获得当前所有快捷键代码 最近的一些面试感悟 这两天被vs2010的std::tr1::bind郁闷了 error C2065: '__LINE__Var' : undeclared identifier 写错名字了 啰嗦几句,关于动机,学习与批评,架构和代码风格 金山卫士代码批评 部门开始做技术talk 数据库的坏味道 --《Refactoring Database: Evolutionary Database Design》读书笔记 偷天换日 初识rails ruby解数独问题
24点计算
Michael Peng · 2010-09-05 · via 博客园 - Michael Peng

在家和太太拿扑克算24点,屡战屡败。为了争口气,于是偷偷写了个小程序来作弊,哈哈。

首先对问题进行分析,24点,就是给定4个操作数,用四则运算符将它们连成合法的算式,可以加括号,以求得24.

碰到的第一个问题就是如何表示算式,常见的算式都是中缀表达式,即运算符在两个操作数之间。中缀表达式的好处是符合人的习惯,容易理解,缺陷则是需要借助额外的括号才能表示清楚

如下面的算式

10-6/2-1

按照运算符优先次序是先计算6/2,再用10减去其结果3,再用刚才的结果7-16

那我们要表示这种优先次序呢?

(10-6)/(2-1)

不加括号时无法用中缀准确表达,一加括号,我们的问题就会引入额外的复杂度。

但后缀表达式则不借助括号就能轻松表达以上的两种优先关系。

10 6 2 / - 1 - 表示10 - 6/2 - 1

10 6 - 2 1 - /表示 (10 - 6) / (2 - 1)

解释:后缀表达式可以看成一个栈操作序列,从左到右扫描表达式,看见一个操作数则压栈,看见一个操作符则从栈上弹出其所需个数的操作数,运算后再将结果压入栈中。

如果用后缀表达式来表示给定4个数所有合法的四则运算式,则其应为4个数全排列中的每一种,和从四种运算符中可重复抽取3个组成的每一种序列,组成的任一合法后缀表达式。

何为合法的后缀表达式?在本文场景下即任一运算符出现时栈中至少有两个操作数。如果去掉第一个操作数,则余下序列中任意一个从头开始的子序列中操作数个数不小于操作符个数。若将其替换为栈操作,操作数看成一个入栈操作,操作符看成一个出栈操作,用X表示入栈,S表示出栈,则对应的序列如XSXSXXSS应是一个合法的操作数序列。

相关代码如下


代码

    public static class Permutation
    {
        
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
        {   
            
foreach(T t in enumerable)
            {
                action(t);
            }
        }
/// <summary>
        
/// delete an object from an object array (use reference equal to compare two objects)
        
/// </summary>
        
/// <param name="objs"></param>
        
/// <param name="v"></param>
        
/// <returns></returns>
        private static Object[] SkipByReference(Object[] objs, Object v)
        {
            
return objs.Where(x => !Object.ReferenceEquals(x, v)).ToArray();
        }
/// <summary>
        
/// generate all permutations of objects in objs
        
/// </summary>
        
/// <param name="objs"></param>
        
/// <returns></returns>
        public static IEnumerable<Object[]> GetPernumtation(Object[] objs)
        {
            Int32 n 
= objs.Length;
            Debug.Assert(n 
> 0);
            
if (n == 1)
            {
                
yield return objs;
            }
            
else
            {
                Object[] current 
= new Object[n];
                
for (int i = 0; i < n; ++i)
                {
                    current[
0= objs[i];
                    
foreach (Object[] next in GetPernumtation(SkipByReference(objs, objs[i])))
                    {
                        Array.Copy(next, 
0, current, 1, n - 1);
                        
yield return current.Clone() as Object[];
                    }
                }
            }
        }
/// <summary>
        
/// Generate repeatable 
        
/// </summary>
        
/// <param name="objs"></param>
        
/// <param name="resultCount"></param>
        
/// <returns></returns>
        public static IEnumerable<Object[]> GetRepeatablePermutation(Object[] objs, int resultCount)
        {
            
if (resultCount == 1)
            {
                
foreach (Object obj in objs)
                {
                    
yield return new Object[] { obj };
                }
            }
            
else
            {
                Object[] result 
= new Object[resultCount];
                
for (int i = 0; i < objs.Length; ++i)
                {
                    result[
0= objs[i];
                    
foreach (Object[] next in GetRepeatablePermutation(objs, resultCount - 1))
                    {
                        Array.Copy(next, 
0, result, 1, resultCount - 1);
                        
yield return result.Clone() as Object[];
                    }
                }
            }
        }
/// <summary>
        
/// Generate all sequences of possible push ans pop in form of String like "XXXYYY"
        
/// X means push and Y means pop
        
/// </summary>
        
/// <param name="n"></param>
        
/// <returns></returns>
        public static IEnumerable<String> GenerateStackOperateSequences(int n)
        {
            Char[] results 
= new Char[2 * n];
            
return GenerateStackOperateSequencesPrivate(results, 0, n, n).Select(x=>new String(x));

        }

private static IEnumerable<Char[]> GenerateStackOperateSequencesPrivate(char[] results, int moreX, int leftX, int leftY)
        {
            
int startIndex = results.Length - leftX - leftY;
            
if (startIndex == results.Length)
            {
                
yield return results.Clone() as char[];
            }
            
if (moreX > 0)
            {
                results[startIndex] 
= 'Y';
                
foreach(var v in GenerateStackOperateSequencesPrivate(results, moreX - 1, leftX, leftY - 1))
                {
                    
yield return v;
                }
            }
            
if (leftX > 0)
            {
                results[startIndex] 
= 'X';
                
foreach (var v in GenerateStackOperateSequencesPrivate(results, moreX + 1, leftX - 1, leftY))
                {
                    
yield return v;
                }
            }
            
yield break;
        }

    }

private static IEnumerable<Instruction[]> GenerateInsructions(Object[] operands,
            Instruction[] instructionCandidates)
        {
            
string[] patterns = Permutation.GenerateStackOperateSequences(operands.Length - 1).Select(x => 'X' + x).ToArray();
            
int instructionCountExpected = operands.Count() - 1;
            List
<Object[]> operantsCandidate = new List<object[]>(
                Permutation.GetPernumtation(operands));
            List
<Object[]> instructionCandiatesList = new List<Object[]>(
                Permutation.GetRepeatablePermutation(instructionCandidates, instructionCountExpected));
            List
<Instruction> result = new List<Instruction>();
            
foreach (Object[] operants in operantsCandidate)
            {
                Queue
<Object> operantsQueue = new Queue<object>(operants);
                
foreach (Object[] instructions in instructionCandiatesList)
                {
                    
foreach (String pattern in patterns)
                    {
                        
int iA = 0;
                        
int iB = 0;

                        result.Clear();

for (int i = 0; i < pattern.Length; ++i)
                        {
                            
if (pattern[i] == 'X')
                            {
                                result.Add(
new LoadInstruction(operants[iA++]));
                            }
                            
else
                            {
                                result.Add(instructions[iB
++as Instruction);
                            }
                        }
                        
yield return result.ToArray();
                    }
                }
            }
        }

至此我们解决了生成所有可能的表达式问题,但还没有考虑如何计算这些表达式。最简单的办法是直接写一个解释器,对表达式中每个元素依次解析。但如果我们增加一种运算,如乘方pow,解释器就需要相应修改。一个更通用的方案是构造一个基于栈的虚拟机,将后缀表达式每个元素替换为一个指令(instruction),操作数替换为load指令,操作符替换为操作指令,再逐条执行指令。

代码

   public abstract class Instruction
    {
        
public abstract String InstructionName { get; }
        
public abstract void Execute(RunTime runtime);/// <summary>
        
/// Simple form of an instruction
        
/// For a load instruction just the number to load
        
/// for arighmetic, just +-*/
        
/// </summary>
        
/// <returns></returns>
        public abstract String GetSimpleForm();
    }
public abstract class BinaryInstruction:Instruction
    {
        
public override string InstructionName
        {
            
get { return "Binary"; }
        }
public override void Execute(RunTime runtime)
        {
            
// op1 is the one push into stack first
            Object operand2 = runtime.Pop();
            Object operand1 
= runtime.Pop();
            runtime.Push(DoExecute(operand1, operand2));
        }
protected abstract Object DoExecute(Object operant1, Object operand2);
    }
public enum ArithmeticOperator
    {
        Add,
        Sub,
        Mul,
        Div,
        Log,
        Pow
    }
    
public class ArithmeticInstruction:BinaryInstruction
    {
        
private ArithmeticOperator _op;
        
public ArithmeticInstruction(ArithmeticOperator op)
        {
            _op 
= op;
        }
protected override Object DoExecute(object operant1, object operand2)
        {
            
double op1 = (double)operant1;
            
double op2 = (double)operand2;
            
switch (_op)
            {
                
case ArithmeticOperator.Add:
                    
return op1 + op2;
                
case ArithmeticOperator.Sub:
                    
return op1 - op2;
                
case ArithmeticOperator.Mul:
                    
return op1 * op2;
                
case ArithmeticOperator.Div:
                    
return op1 / op2;
            }
            
throw new NotImplementedException();
        }
public override string ToString()
        {
            
return _op.ToString();
        }
public override String GetSimpleForm()
        {
            
switch (_op)
            {
                
case ArithmeticOperator.Add:
                    
return "+";
                
case ArithmeticOperator.Sub:
                    
return "-";
                
case ArithmeticOperator.Mul:
                    
return "*";
                
case ArithmeticOperator.Div:
                    
return "/";
            }
            
throw new NotImplementedException();
        }
    }
public class LoadInstruction:Instruction
    {
        
private Object _value;
        
public LoadInstruction(Object value)
        {
            _value 
= value;
        }
        
public override string InstructionName
        {
            
get { return "Load"; }
        }
public override void Execute(RunTime runtime)
        {
            runtime.Push(_value);
        }
public override string ToString()
        {
            
return String.Format("{0}:{1}", InstructionName, _value == null ? String.Empty : _value.ToString());
        }
public override string GetSimpleForm()
        {
            
return _value.ToString();
        }

    }

public class RunTime
    {
        Stack
<Object> _stack = new Stack<object>();
        
public void Push(Object v)
        {
            _stack.Push(v);
        }
public Object Pop()
        {
            
return  _stack.Pop();
        }
public Object Execute(Instruction[] instructions)
        {
            instructions.ForEach(x
=>x.Execute(this));
            
return Pop();
        }
    }

最后还需要解决一个问题,如何把得到的指令集以便于阅读的方式输出

代码

    class PostOrderToInOrderConvertor
    {
        Stack<String> _stack = new Stack<String>();
        
        
public String Convert(Instruction[] instructions)
        {
            
foreach (var v in instructions)
            {
                
if (v is LoadInstruction)
                {
                    _stack.Push(v.GetSimpleForm());
                }
                
else if (v is BinaryInstruction)
                {
                    String s2 = PopNormalized();
                    String s1 = PopNormalized();
                    _stack.Push(s1 + " " + v.GetSimpleForm() + " " + s2);
                }
            }
            
return _stack.Pop();
        }private String PopNormalized()
        {
            
return AddBrackIfNeeded(_stack.Pop());
        }private static String AddBrackIfNeeded(String s)
        {
            
if (s.IndexOf(' '>= 0)
            {
                
return "(" + s + ")";
            }
            
else
            {
                
return s;
            }
        }
    }

 稍微对程序做了下扩展,可以支持任意n个数四则运算求某个值

源码下载