非确定性计算引擎转化为C#版本并重构

比眉伴天荒 2022-03-28 04:23 183阅读 0赞

这是之前我写的原始的 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 之间就有线程同步的开销,如前文所说。这个性能优化只好留待后面有空再继续研究。

下面是目前的状况的实现代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Collections;
  7. namespace NonDeterministicEngineCS
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. Benchmarking(new Action(Test1), "Test1() 执行完成,花费:{0}毫秒。");
  14. Console.WriteLine("====================================================");
  15. Benchmarking(new Action(Test2), "Test2() 执行完成,花费:{0}毫秒。");
  16. Console.WriteLine("====================================================");
  17. Benchmarking(new Action(Test3), "Test3() 执行完成,花费:{0}毫秒。");
  18. Console.ReadLine();
  19. }
  20. // 一个简单的测试例子
  21. public static void Test1()
  22. {
  23. NonDeterministicEngine engine = new NonDeterministicEngine();
  24. engine.AddParam("a", new int[] { 1, 2, 3, 4, 5, 6 });
  25. engine.AddParam("b", new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
  26. engine.AddRequire((int a) => a > 2 && a < 9);
  27. engine.AddRequire((int b) => b > 5 && b <= 10);
  28. engine.AddRequire((int a, int b) => a == b - 1);
  29. engine.EachResult(
  30. result => Console.WriteLine("a = {0}, b = {1}", result["a"], result["b"]));
  31. }
  32. // 爱因斯坦谜题
  33. public static void Test2()
  34. {
  35. NonDeterministicEngine engine = new NonDeterministicEngine();
  36. engine.AddParam("baker", new int[] { 1, 2, 3, 4, 5 });
  37. engine.AddParam("cooper", new int[] { 1, 2, 3, 4, 5 });
  38. engine.AddParam("fletcher", new int[] { 1, 2, 3, 4, 5 });
  39. engine.AddParam("miller", new int[] { 1, 2, 3, 4, 5 });
  40. engine.AddParam("smith", new int[] { 1, 2, 3, 4, 5 });
  41. engine.AddRequire((int baker) => baker != 5);
  42. engine.AddRequire((int cooper) => cooper != 1);
  43. engine.AddRequire((int fletcher) => fletcher != 1 && fletcher != 5);
  44. engine.AddRequire((int miller, int cooper) => miller > cooper);
  45. engine.AddRequire((int smith, int fletcher) =>
  46. smith != fletcher + 1
  47. && smith != fletcher - 1);
  48. engine.AddRequire((int fletcher, int cooper) => fletcher != cooper + 1
  49. && fletcher != cooper - 1);
  50. engine.AddRequire((int baker, int cooper, int fletcher, int miller, int smith) =>
  51. baker != cooper && baker != fletcher && baker != miller
  52. && baker != smith && cooper != fletcher && cooper != miller
  53. && cooper != smith && fletcher != miller && fletcher != smith && miller != smith);
  54. engine.EachResult(
  55. result =>
  56. Console.WriteLine("baker: {0}, cooper: {1}, fletcher: {2}, miller: {3}, smith: {4}",
  57. result["baker"],
  58. result["cooper"],
  59. result["fletcher"],
  60. result["miller"],
  61. result["smith"])
  62. );
  63. }
  64. // 八皇后问题的解法
  65. public static void Test3()
  66. {
  67. var engine = new NonDeterministicEngine();
  68. engine.AddParam("a", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  69. engine.AddParam("b", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  70. engine.AddParam("c", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  71. engine.AddParam("d", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  72. engine.AddParam("e", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  73. engine.AddParam("f", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  74. engine.AddParam("g", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  75. engine.AddParam("h", new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
  76. engine.AddRequire((int a, int b, int c, int d, int e, int f, int g, int h) =>
  77. a != b && a != c && a != d
  78. && a != e && a != f && a != g && a != h
  79. && b != c && b != d && b != e && b != f && b != g && b != h
  80. && c != d && c != e && c != f && c != g && c != h
  81. && d != e && d != f && d != g && d != h
  82. && e != f && e != g && e != h
  83. && f != g && f != h
  84. && g != h
  85. && NotInTheSameDiagonalLine(new int[] { a, b, c, d, e, f, g, h }));
  86. engine.EachResult(
  87. result =>
  88. Console.WriteLine("(1,{0}), (2,{1}), (3,{2}), (4,{3}), (5,{4}), (6,{5}), (7,{6}), (8,{7})",
  89. result["a"],
  90. result["b"],
  91. result["c"],
  92. result["d"],
  93. result["e"],
  94. result["f"],
  95. result["g"],
  96. result["h"])
  97. );
  98. }
  99. static bool NotInTheSameDiagonalLine(int[] cols)
  100. {
  101. for (int i = 0; i < cols.Length - 1; i++)
  102. {
  103. for (int j = i + 1; j < cols.Length; j++)
  104. {
  105. if (j - i == Math.Abs(cols[j] - cols[i]))
  106. return false;
  107. }
  108. }
  109. return true;
  110. }
  111. public static void Benchmarking(Action f, string messageFormat)
  112. {
  113. DateTime time1 = DateTime.Now;
  114. f();
  115. DateTime time2 = DateTime.Now;
  116. Console.WriteLine(messageFormat, (time2 - time1).TotalMilliseconds);
  117. }
  118. }
  119. }
  120. using System;
  121. using System.Collections.Generic;
  122. using System.Linq;
  123. using System.Text;
  124. using System.Collections;
  125. namespace NonDeterministicEngineCS
  126. {
  127. public class Param
  128. {
  129. public string Name
  130. {
  131. get;
  132. set;
  133. }
  134. public IEnumerator Values
  135. {
  136. get;
  137. set;
  138. }
  139. }
  140. }
  141. using System;
  142. using System.Collections.Generic;
  143. using System.Linq;
  144. using System.Text;
  145. namespace NonDeterministicEngineCS
  146. {
  147. public abstract class Condition
  148. {
  149. public IList<string> ParamNames
  150. {
  151. get;
  152. protected set;
  153. }
  154. public abstract bool Call(params object[] args);
  155. public Condition(Delegate predicate)
  156. {
  157. ParamNames = predicate.Method.GetParameters().Select(p => p.Name).ToArray();
  158. }
  159. }
  160. public class Condition<T> : Condition
  161. {
  162. public Condition(Func<T, bool> predicate)
  163. : base(predicate)
  164. {
  165. m_func = predicate;
  166. }
  167. Func<T, bool> m_func;
  168. public override bool Call(params object[] args)
  169. {
  170. return m_func((T)args[0]);
  171. }
  172. }
  173. public class Condition<T1, T2> : Condition
  174. {
  175. public Condition(Func<T1, T2, bool> predicate)
  176. : base(predicate)
  177. {
  178. m_func = predicate;
  179. }
  180. Func<T1, T2, bool> m_func;
  181. public override bool Call(params object[] args)
  182. {
  183. return m_func((T1)args[0], (T2)args[1]);
  184. }
  185. }
  186. public class Condition<T1, T2, T3> : Condition
  187. {
  188. public Condition(Func<T1, T2, T3, bool> predicate)
  189. : base(predicate)
  190. {
  191. m_func = predicate;
  192. }
  193. Func<T1, T2, T3, bool> m_func;
  194. public override bool Call(params object[] args)
  195. {
  196. return m_func((T1)args[0], (T2)args[1], (T3)args[2]);
  197. }
  198. }
  199. public class Condition<T1, T2, T3, T4> : Condition
  200. {
  201. public Condition(Func<T1, T2, T3, T4, bool> predicate)
  202. : base(predicate)
  203. {
  204. m_func = predicate;
  205. }
  206. Func<T1, T2, T3, T4, bool> m_func;
  207. public override bool Call(params object[] args)
  208. {
  209. return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4) args[3]);
  210. }
  211. }
  212. public class Condition<T1, T2, T3, T4, T5> : Condition
  213. {
  214. public Condition(Func<T1, T2, T3, T4, T5, bool> predicate)
  215. : base(predicate)
  216. {
  217. m_func = predicate;
  218. }
  219. Func<T1, T2, T3, T4, T5, bool> m_func;
  220. public override bool Call(params object[] args)
  221. {
  222. return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4]);
  223. }
  224. }
  225. public class Condition<T1, T2, T3, T4, T5, T6> : Condition
  226. {
  227. public Condition(Func<T1, T2, T3, T4, T5, T6, bool> predicate)
  228. : base(predicate)
  229. {
  230. m_func = predicate;
  231. }
  232. Func<T1, T2, T3, T4, T5, T6, bool> m_func;
  233. public override bool Call(params object[] args)
  234. {
  235. return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], (T6)args[5]);
  236. }
  237. }
  238. public class Condition<T1, T2, T3, T4, T5, T6, T7> : Condition
  239. {
  240. public Condition(Func<T1, T2, T3, T4, T5, T6, T7, bool> predicate)
  241. : base(predicate)
  242. {
  243. m_func = predicate;
  244. }
  245. Func<T1, T2, T3, T4, T5, T6, T7, bool> m_func;
  246. public override bool Call(params object[] args)
  247. {
  248. return m_func((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], (T6)args[5], (T7)args[6]);
  249. }
  250. }
  251. public class Condition<T1, T2, T3, T4, T5, T6, T7, T8> : Condition
  252. {
  253. public Condition(Func<T1, T2, T3, T4, T5, T6, T7, T8, bool> predicate)
  254. : base(predicate)
  255. {
  256. m_func = predicate;
  257. }
  258. Func<T1, T2, T3, T4, T5, T6, T7, T8, bool> m_func;
  259. public override bool Call(params object[] args)
  260. {
  261. 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]);
  262. }
  263. }
  264. }
  265. using System;
  266. using System.Collections.Generic;
  267. using System.Linq;
  268. using System.Text;
  269. using System.Collections;
  270. using System.Threading.Tasks;
  271. using System.Linq.Expressions;
  272. namespace NonDeterministicEngineCS
  273. {
  274. public class NonDeterministicEngine
  275. {
  276. private List<Param> m_paramDict = new List<Param>();
  277. private List<Condition> m_predicateDict = new List<Condition>();
  278. public void AddParam(string name, IEnumerable values)
  279. {
  280. m_paramDict.Add(new Param { Name = name, Values = values.GetEnumerator() });
  281. }
  282. #region Add Require
  283. public void AddRequire<T>(Func<T, bool> predicate)
  284. {
  285. m_predicateDict.Add(new Condition<T>(predicate));
  286. }
  287. public void AddRequire<T1, T2>(Func<T1, T2, bool> predicate)
  288. {
  289. m_predicateDict.Add(new Condition<T1, T2>(predicate));
  290. }
  291. public void AddRequire<T1, T2, T3>(Func<T1, T2, T3, bool> predicate)
  292. {
  293. m_predicateDict.Add(new Condition<T1, T2, T3>(predicate));
  294. }
  295. public void AddRequire<T1, T2, T3, T4>(Func<T1, T2, T3, T4, bool> predicate)
  296. {
  297. m_predicateDict.Add(new Condition<T1, T2, T3, T4>(predicate));
  298. }
  299. public void AddRequire<T1, T2, T3, T4, T5>(Func<T1, T2, T3, T4, T5, bool> predicate)
  300. {
  301. m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5>(predicate));
  302. }
  303. public void AddRequire<T1, T2, T3, T4, T5, T6>(Func<T1, T2, T3, T4, T5, T6, bool> predicate)
  304. {
  305. m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5, T6>(predicate));
  306. }
  307. public void AddRequire<T1, T2, T3, T4, T5, T6, T7>(Func<T1, T2, T3, T4, T5, T6, T7, bool> predicate)
  308. {
  309. m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5, T6, T7>(predicate));
  310. }
  311. public void AddRequire<T1, T2, T3, T4, T5, T6, T7, T8>(Func<T1, T2, T3, T4, T5, T6, T7, T8, bool> predicate)
  312. {
  313. m_predicateDict.Add(new Condition<T1, T2, T3, T4, T5, T6, T7, T8>(predicate));
  314. }
  315. #endregion
  316. public IEnumerable<Dictionary<string, object>> GetResults()
  317. {
  318. var em = new CombinationEnumerable(this);
  319. foreach (var item in em)
  320. {
  321. if (Satisfy(item))
  322. yield return item;
  323. }
  324. }
  325. public void EachResult(Action<Dictionary<string, object>> action)
  326. {
  327. var em = new CombinationEnumerable(this);
  328. Parallel.ForEach(
  329. em,
  330. result =>
  331. {
  332. if (Satisfy(result))
  333. {
  334. action(result);
  335. }
  336. }
  337. );
  338. }
  339. bool Satisfy(Dictionary<string, object> result)
  340. {
  341. foreach (Condition item in m_predicateDict)
  342. {
  343. var args = item.ParamNames.Select(
  344. name => result[name]
  345. ).ToArray();
  346. if (item.Call(args))
  347. continue;
  348. return false;
  349. }
  350. return true;
  351. }
  352. private class CombinationEnumerable : IEnumerable<Dictionary<string, object>>
  353. {
  354. NonDeterministicEngine m_engine;
  355. public CombinationEnumerable(NonDeterministicEngine engine)
  356. {
  357. m_engine = engine;
  358. }
  359. public IEnumerator<Dictionary<string, object>> GetEnumerator()
  360. {
  361. return new CombinationEnumerator(m_engine);
  362. }
  363. IEnumerator IEnumerable.GetEnumerator()
  364. {
  365. return new CombinationEnumerator(m_engine);
  366. }
  367. }
  368. /// <summary>
  369. /// 组合多个 iterator 为一个复合的 iterator.
  370. /// MoveNext 实现为:移动到下一个所有变量值可能的组合。
  371. /// </summary>
  372. private class CombinationEnumerator : IEnumerator<Dictionary<string, object>>
  373. {
  374. private bool m_firstTime = true;
  375. private NonDeterministicEngine m_target;
  376. public CombinationEnumerator(NonDeterministicEngine engine)
  377. {
  378. m_target = engine;
  379. }
  380. public void Dispose()
  381. {
  382. }
  383. public Dictionary<string, object> GetCurrent()
  384. {
  385. if (IterationOver)
  386. return null;
  387. return m_target.m_paramDict.ToDictionary
  388. (param => param.Name, param => param.Values.Current);
  389. }
  390. public bool MoveNext()
  391. {
  392. if (IterationOver)
  393. return false;
  394. if (m_firstTime)
  395. {
  396. // 首次执行时,需要将所有变量的 enumerator 都前进到起始位置
  397. foreach (Param item in m_target.m_paramDict)
  398. {
  399. item.Values.MoveNext();
  400. }
  401. m_firstTime = false;
  402. return true;
  403. }
  404. // 首先尝试最后一个变量的 iterator,看能否 MoveNext() (是否还有没有尝试过的值)。
  405. int iterIndex = m_target.m_paramDict.Count - 1;
  406. bool canMoveNext = m_target.m_paramDict[iterIndex].Values.MoveNext();
  407. if (canMoveNext)
  408. return true;
  409. // 否则依次回溯到前一个变量,看该变量的 iterator 能否 MoveNext()
  410. while (!canMoveNext)
  411. {
  412. iterIndex--;
  413. // 表明已尝试了所有变量的所有可能值,退无可退,则终止枚举过程。
  414. if (iterIndex == -1)
  415. {
  416. IterationOver = true;
  417. return false;
  418. }
  419. canMoveNext = m_target.m_paramDict[iterIndex].Values.MoveNext();
  420. // 如果往前退到某个可以前进的位置
  421. if (canMoveNext)
  422. {
  423. // 则需要将这个位置之后的所有其他变量的 iterator 复位,并前进到第一种可能性。
  424. for (int i = iterIndex + 1; i < m_target.m_paramDict.Count; i++)
  425. {
  426. var iter = m_target.m_paramDict[i].Values;
  427. iter.Reset();
  428. iter.MoveNext();
  429. }
  430. return true;
  431. }
  432. }
  433. return false;
  434. }
  435. public void Reset()
  436. {
  437. IterationOver = false;
  438. m_firstTime = true;
  439. foreach (var param in m_target.m_paramDict)
  440. {
  441. param.Values.Reset();
  442. }
  443. }
  444. public Dictionary<string, object> Current
  445. {
  446. get
  447. {
  448. return GetCurrent();
  449. }
  450. }
  451. /// <summary>
  452. /// 标记枚举是否已经结束
  453. /// </summary>
  454. public bool IterationOver
  455. {
  456. get;
  457. set;
  458. }
  459. object IEnumerator.Current
  460. {
  461. get
  462. {
  463. return GetCurrent();
  464. }
  465. }
  466. }
  467. }
  468. }

发表评论

表情:
评论列表 (有 0 条评论,183人围观)

还没有评论,来说两句吧...

相关阅读

    相关 确定性函数和确定性函数

    比如:ABS 返回给定数字表达式的绝对值,每次输入相同的参数值,所得的结果都是相同的,所以它是确定函数;而 GETDATA 返回当前系统时间,每次调用的结果都不同,所以它是非确