非確定性計算引擎轉化為C#版本並重構

weixin_34015860發表於2010-07-06

這是之前我寫的原始的 VB.NET 版本:

http://www.cnblogs.com/RChen/archive/2010/05/17/1737587.html

 

轉化為 C# 版本後,還進行了一些重構。包括修改成了強型別,以及使用了 Parallel.ForEach,但是發現沒有收到預期的效果。效能提升比較少。

研究後發現,其實問題的關鍵在於要通過某種方式對遍歷的可能性進行剪枝,這樣才能減少遍歷次數,從而提升效能。而且,由於結果是通過 yield return 和 IEnumerable 實現的,並沒有實現 IList 或者 Array. 所以它本質上並不支援按索引範圍拆分的 Parallel.ForEach 工作方式,而實際估計是使用的幾個 chunk 輪番讀取的低效方式,這樣在各個 chunk 之間就有執行緒同步的開銷,如前文所說。這個效能優化只好留待後面有空再繼續研究。

 

下面是目前的狀況的實現程式碼:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;

namespace NonDeterministicEngineCS
{
    class Program
    {
        static void Main(string[] args)
        {
            Benchmarking(new Action(Test1), "Test1() 執行完成,花費:{0}毫秒。");
            Console.WriteLine("====================================================");
            Benchmarking(new Action(Test2), "Test2() 執行完成,花費:{0}毫秒。");
            Console.WriteLine("====================================================");
            Benchmarking(new Action(Test3), "Test3() 執行完成,花費:{0}毫秒。");
            Console.ReadLine();
        }

        // 一個簡單的測試例子
        public static void Test1()
        {
            NonDeterministicEngine engine = new NonDeterministicEngine();

            engine.AddParam("a", new int[] { 1, 2, 3, 4, 5, 6 });
            engine.AddParam("b", new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });

            engine.AddRequire((int a) => a > 2 && a < 9);
            engine.AddRequire((int b) => b > 5 && b <= 10);
            engine.AddRequire((int a, int b) => a == b - 1);

            engine.EachResult(
                result => Console.WriteLine("a = {0}, b = {1}", result["a"], result["b"]));
        }

        // 愛因斯坦謎題
        public static void Test2()
        {
            NonDeterministicEngine engine = new NonDeterministicEngine();

            engine.AddParam("baker", new int[] { 1, 2, 3, 4, 5 });
            engine.AddParam("cooper", new int[] { 1, 2, 3, 4, 5 });
            engine.AddParam("fletcher", new int[] { 1, 2, 3, 4, 5 });
            engine.AddParam("miller", new int[] { 1, 2, 3, 4, 5 });
            engine.AddParam("smith", new int[] { 1, 2, 3, 4, 5 });

            engine.AddRequire((int baker) => baker != 5);
            engine.AddRequire((int cooper) => cooper != 1);
            engine.AddRequire((int fletcher) => fletcher != 1 && fletcher != 5);
            engine.AddRequire((int miller, int cooper) => miller > cooper);
            engine.AddRequire((int smith, int fletcher) =>
                smith != fletcher + 1
                && smith != fletcher - 1);

            engine.AddRequire((int fletcher, int cooper) => fletcher != cooper + 1
                && fletcher != cooper - 1);

            engine.AddRequire((int baker, int cooper, int fletcher, int miller, int smith) =>
                baker != cooper && baker != fletcher && baker != miller
                && baker != smith && cooper != fletcher && cooper != miller
                && cooper != smith && fletcher != miller && fletcher != smith && miller != smith);

            engine.EachResult(
             result =>
                 Console.WriteLine("baker: {0}, cooper: {1}, fletcher: {2}, miller: {3}, smith: {4}",
                         result["baker"],
                         result["cooper"],
                         result["fletcher"],
                         result["miller"],
                         result["smith"])
            );
        }

        // 八皇后問題的解法
        public static void Test3()
        {
            var engine = new NonDeterministicEngine();

            engine.AddParam("a", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            engine.AddParam("b", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            engine.AddParam("c", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            engine.AddParam("d", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            engine.AddParam("e", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            engine.AddParam("f", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            engine.AddParam("g", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            engine.AddParam("h", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });

            engine.AddRequire((int a, int b, int c, int d, int e, int f, int g, int h) =>
                a != b && a != c && a != d
                && a != e && a != f && a != g && a != h
                && b != c && b != d && b != e && b != f && b != g && b != h
                && c != d && c != e && c != f && c != g && c != h
                && d != e && d != f && d != g && d != h
                && e != f && e != g && e != h
                && f != g && f != h
                && g != h
                && NotInTheSameDiagonalLine(new int[] { a, b, c, d, e, f, g, h }));

            engine.EachResult(
               result =>
                   Console.WriteLine("(1,{0}), (2,{1}), (3,{2}), (4,{3}), (5,{4}), (6,{5}), (7,{6}), (8,{7})",
                   result["a"],
                   result["b"],
                   result["c"],
                   result["d"],
                   result["e"],
                   result["f"],
                   result["g"],
                   result["h"])
                   );
        }

        static bool NotInTheSameDiagonalLine(int[] cols)
        {
            for (int i = 0; i < cols.Length - 1; i++)
            {
                for (int j = i + 1; j < cols.Length; j++)
                {
                    if (j - i == Math.Abs(cols[j] - cols[i]))
                        return false;
                }
            }
            return true;
        }

        public static void Benchmarking(Action f, string messageFormat)
        {
            DateTime time1 = DateTime.Now;
            f();
            DateTime time2 = DateTime.Now;
            Console.WriteLine(messageFormat, (time2 - time1).TotalMilliseconds);
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace NonDeterministicEngineCS
{
    public class Param
    {
        public string Name
        {
            get;
            set;
        }
        public IEnumerator Values
        {
            get;
            set;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NonDeterministicEngineCS
{
    public abstract class Condition
    {
        public IList<string> ParamNames
        {
            get;
            protected set;
        }
        
        public abstract bool Call(params object[] args);

        public Condition(Delegate predicate)
        {
            ParamNames = predicate.Method.GetParameters().Select(p => p.Name).ToArray();
        }
    }

    public class Condition<T> : Condition
    {
        public Condition(Func<T, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }

        Func<T, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T)args[0]);
        }
    }

    public class Condition<T1, T2> : Condition
    {
        public Condition(Func<T1, T2, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }

        Func<T1, T2, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T1)args[0], (T2)args[1]);
        }
    }

    public class Condition<T1, T2, T3> : Condition
    {
        public Condition(Func<T1, T2, T3, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }

        Func<T1, T2, T3, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T1)args[0], (T2)args[1], (T3)args[2]);
        }
    }

    public class Condition<T1, T2, T3, T4> : Condition
    {
        public Condition(Func<T1, T2, T3, T4, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }

        Func<T1, T2, T3, T4, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4) args[3]);
        }
    }

    public class Condition<T1, T2, T3, T4, T5> : Condition
    {
        public Condition(Func<T1, T2, T3, T4, T5, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }

        Func<T1, T2, T3, T4, T5, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4]);
        }
    }

    public class Condition<T1, T2, T3, T4, T5, T6> : Condition
    {
        public Condition(Func<T1, T2, T3, T4, T5, T6, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }

        Func<T1, T2, T3, T4, T5, T6, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], (T6)args[5]);
        }
    }

    public class Condition<T1, T2, T3, T4, T5, T6, T7> : Condition
    {
        public Condition(Func<T1, T2, T3, T4, T5, T6, T7, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }

        Func<T1, T2, T3, T4, T5, T6, T7, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], (T6)args[5], (T7)args[6]);
        }
    }

    public class Condition<T1, T2, T3, T4, T5, T6, T7, T8> : Condition
    {
        public Condition(Func<T1, T2, T3, T4, T5, T6, T7, T8, bool> predicate)
            : base(predicate)
        {
            m_func = predicate;
        }
        
        Func<T1, T2, T3, T4, T5, T6, T7, T8, bool> m_func;

        public override bool Call(params object[] args)
        {
            return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], (T6)args[5], (T7)args[6], (T8)args[7]);
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading.Tasks;
using System.Linq.Expressions;

namespace NonDeterministicEngineCS
{
    public class NonDeterministicEngine
    {
        private List<Param> m_paramDict = new List<Param>();
        private List<Condition> m_predicateDict = new List<Condition>();

        public void AddParam(string name, IEnumerable values)
        {
            m_paramDict.Add(new Param { Name = name, Values = values.GetEnumerator() });
        }

        #region Add Require
        public void AddRequire<T>(Func<T, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T>(predicate));
        }

        public void AddRequire<T1, T2>(Func<T1, T2, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T1, T2>(predicate));
        }

        public void AddRequire<T1, T2, T3>(Func<T1, T2, T3, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T1, T2, T3>(predicate));
        }

        public void AddRequire<T1, T2, T3, T4>(Func<T1, T2, T3, T4, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T1, T2, T3, T4>(predicate));
        }

        public void AddRequire<T1, T2, T3, T4, T5>(Func<T1, T2, T3, T4, T5, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5>(predicate));
        }

        public void AddRequire<T1, T2, T3, T4, T5, T6>(Func<T1, T2, T3, T4, T5, T6, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5, T6>(predicate));
        }

        public void AddRequire<T1, T2, T3, T4, T5, T6, T7>(Func<T1, T2, T3, T4, T5, T6, T7, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5, T6, T7>(predicate));
        }

        public void AddRequire<T1, T2, T3, T4, T5, T6, T7, T8>(Func<T1, T2, T3, T4, T5, T6, T7, T8, bool> predicate)
        {
            m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5, T6, T7, T8>(predicate));
        } 
        #endregion

        public IEnumerable<Dictionary<string, object>> GetResults()
        {
            var em = new CombinationEnumerable(this);

            foreach (var item in em)
            {
                if (Satisfy(item))
                    yield return item;
            }
        }

        public void EachResult(Action<Dictionary<string, object>> action)
        {
            var em = new CombinationEnumerable(this);

            Parallel.ForEach(
                em,
                result =>
                {
                    if (Satisfy(result))
                    {
                        action(result);
                    }
                }
            );
        }
        
        bool Satisfy(Dictionary<string, object> result)
        {
            foreach (Condition item in m_predicateDict)
            {
                var args = item.ParamNames.Select(
                    name => result[name]
                        ).ToArray();

                if (item.Call(args))
                    continue;

                return false;
            }
            return true;
        }
        
        private class CombinationEnumerable : IEnumerable<Dictionary<string, object>>
        {
            NonDeterministicEngine m_engine;

            public CombinationEnumerable(NonDeterministicEngine engine)
            {
                m_engine = engine;
            }

            public IEnumerator<Dictionary<string, object>> GetEnumerator()
            {
                return new CombinationEnumerator(m_engine);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return new CombinationEnumerator(m_engine);
            }
        }

        /// <summary>
        /// 組合多個 iterator 為一個複合的 iterator.
        /// MoveNext 實現為:移動到下一個所有變數值可能的組合。
        /// </summary>
        private class CombinationEnumerator : IEnumerator<Dictionary<string, object>>
        {
            private bool m_firstTime = true;
            private NonDeterministicEngine m_target;

            public CombinationEnumerator(NonDeterministicEngine engine)
            {
                m_target = engine;
            }

            public void Dispose()
            {
            }

            public Dictionary<string, object> GetCurrent()
            {
                if (IterationOver)
                    return null;

                return m_target.m_paramDict.ToDictionary
                    (param => param.Name, param => param.Values.Current);
            }

            public bool MoveNext()
            {
                if (IterationOver)
                    return false;

                if (m_firstTime)
                {
                    // 首次執行時,需要將所有變數的 enumerator 都前進到起始位置
                    foreach (Param item in m_target.m_paramDict)
                    {
                        item.Values.MoveNext();
                    }
                    m_firstTime = false;
                    return true;
                }

                // 首先嚐試最後一個變數的 iterator,看能否 MoveNext() (是否還有沒有嘗試過的值)。
                int iterIndex = m_target.m_paramDict.Count - 1;
                bool canMoveNext = m_target.m_paramDict[iterIndex].Values.MoveNext();
                if (canMoveNext)
                    return true;

                // 否則依次回溯到前一個變數,看該變數的 iterator 能否 MoveNext()
                while (!canMoveNext)
                {
                    iterIndex--;

                    // 表明已嘗試了所有變數的所有可能值,退無可退,則終止列舉過程。
                    if (iterIndex == -1)
                    {
                        IterationOver = true;
                        return false;
                    }

                    canMoveNext = m_target.m_paramDict[iterIndex].Values.MoveNext();
                    // 如果往前退到某個可以前進的位置
                    if (canMoveNext)
                    {
                        // 則需要將這個位置之後的所有其他變數的 iterator 復位,並前進到第一種可能性。
                        for (int i = iterIndex + 1; i < m_target.m_paramDict.Count; i++)
                        {
                            var iter = m_target.m_paramDict[i].Values;
                            iter.Reset();
                            iter.MoveNext();
                        }
                        return true;
                    }
                }
                return false;
            }
            
            public void Reset()
            {
                IterationOver = false;
                m_firstTime = true;

                foreach (var param in m_target.m_paramDict)
                {
                    param.Values.Reset();
                }
            }

            public Dictionary<string, object> Current
            {
                get
                {
                    return GetCurrent();
                }
            }
            
            /// <summary>
            /// 標記列舉是否已經結束
            /// </summary>
            public bool IterationOver
            {
                get;
                set;
            }

            object IEnumerator.Current
            {
                get
                {
                    return GetCurrent();
                }
            }
        }
    }
}

相關文章