Darkest page of my coding life

The code i wrote a while ago recently caused a disaster and as I reviewed it I found it is the silliest code I've ever written,

 1 static int BadMaximalMatch(TListRef list1, int start1, int count1, TListRef list2, int start2, int count2, 

 2     const IEqualityComparer<T> &comparer, List<int> &indices1, List<int> &indices2)

 3 {

 4     if (count1 <= 0 || count2 <= 0)

 5     {

 6         return 0;

 7     }

 8 

 9     bool eq = comparer.Equals(list1[start1], list2[start2]);

10     if (eq)

11     {

12         indices1.Add(start1);

13         indices2.Add(start2);

14 

15         return BadMaximalMatch(list1, start1+1, count1-1, list2, start2+1, count2-1, comparer, indices1, indices2) + 1;

16     }

17     else

18     {

19         bool eq01 = count2 >= 2 && comparer.Equals(list1[start1], list2[start2+1]);

20         bool eq10 = count1 >= 2 && comparer.Equals(list1[start1+1], list2[start2]);

21 

22         int crossDiff;

23         if (eq01 && eq10)

24         {

25             crossDiff = 1;

26         }

27         else if (eq01 && !eq10)

28         {

29             return BadMaximalMatch(list1, start1, count1, list2, start2+1, count2-1, comparer, indices1, indices2);

30         }

31         else if (!eq01 && eq10)

32         {

33             return BadMaximalMatch(list1, start1+1, count1-1, list2, start2, count2, comparer, indices1, indices2);

34         }

35         else

36         {

37             bool eq11 = count1 >= 2 && count2 >= 2 && comparer.Equals(list1[start1+1], list2[start2+1]);

38             if (eq11)

39             {

40                 indices1.Add(start1+1);

41                 indices2.Add(start2+1);

42                 return BadMaximalMatch(list1, start1+2, count1-2, list2, start2+2, count2-2, comparer, indices1, indices2)+1;

43             }

44             crossDiff = 2;

45         }

46 

47         List<int> temp11, temp12, temp21, temp22;

48         int m1 = 0, m2 = 0;

49         if (count1 < count2)

50         {

51             // calculate m1 first, as maximum of m1 is greater than that of m2

52             // maximum: min(count1, count2-crossDiff)

53             m1 = BadMaximalMatch(list1, start1, count1, list2, start2+crossDiff, count2-crossDiff, comparer, temp11, temp12);

54             if (m1 < count1 && m1 < count2-crossDiff)

55             {

56                 // m1 hasn't reached its maximum possible value

57                 // maximum: min(count1-crossDiff, count2) 

58                 m2 = BadMaximalMatch(list1, start1+crossDiff, count1-crossDiff, list2, start2, count2, comparer, temp21, temp22);

59             }

60         }

61         else

62         {

63             // calculate m2 first, as maximum of m2 is greater than that of m1

64             m2 = BadMaximalMatch(list1, start1+crossDiff, count1-crossDiff, list2, start2, count2, comparer, temp21, temp22);

65             if (m2 < count2 && m2 < count1-crossDiff)

66             {

67                 // m2 hasn't reached its maximum possible value

68                 // maximum: min(count1, count2-crossDiff)

69                 m1 = BadMaximalMatch(list1, start1, count1, list2, start2+crossDiff, count2-crossDiff, comparer, temp11, temp12);

70             }

71         }

72             

73         if (m2 > m1)

74         {

75             for (int i = 0; i < m2; i++)

76             {

77                 indices1.Add(temp21[i]);

78                 indices2.Add(temp22[i]);

79             }

80             return m2;

81         }

82         else

83         {

84             for (int i = 0; i < m1; i++)

85             {

86                 indices1.Add(temp11[i]);

87                 indices2.Add(temp12[i]);

88             }

89             return m1;

90         }

91     }

92 }

It simply finds out the maximum common sublist of two. And I was dumb enough to not realize it was a very simple problem and be spending quite a while on a complex recursive algorithm as above to solve that. So far there's no evidence it's buggy, but it's as bad as buggy when dealing with just more than 20 data points. A random test today showed that the code above is problematic in that it doesn't take into account some of the possible options that goes across the one that it believes is optimal. Basically the algorithm should only step forward when the current item from either list has no match in the other. So the correct one should be

 1 static int MaximalSublistMatch_Slow(TListRef list1, int start1, int count1, TListRef list2, int start2, int count2,

 2     const IEqualityComparer<T> &comparer, List<int> &indices1, List<int> &indices2)

 3 {

 4     if (count1 <= 0 || count2 <= 0)

 5     {

 6         return 0;

 7     }

 8 

 9     bool eq = comparer.Equals(list1[start1], list2[start2]);

10     if (eq)

11     {

12         indices1.Add(start1);

13         indices2.Add(start2);

14 

15         return MaximalSublistMatch_Slow(list1, start1 + 1, count1 - 1, list2, start2 + 1, count2 - 1, comparer, indices1, indices2) + 1;

16     }

17     else

18     {

19         const T &v1 = list1[start1];

20         const T &v2 = list2[start2];

21 

22         int l1Match=-1, l2Match=-1;

23         for (int i = start2 + 1; i < start2+count2; i++)

24         {

25             if (list2[i] == v1)

26             {

27                 l1Match = i;

28                 break;

29             }

30         }

31 

32         for (int i = start1 + 1; i < start1+count1; i++)

33         {

34             if (list1[i] == v2)

35             {

36                 l2Match = i;

37                 break;

38             }

39         }

40 

41         if (l1Match < 0 && l2Match < 0)

42         {

43             return MaximalSublistMatch_Slow(list1, start1 + 1, count1 - 1, list2, start2 + 1, count2 - 1, comparer, indices1, indices2);

44         }

45         else

46         {

47             // try both

48             List<int> temp11, temp12, temp21, temp22;

49             int r2 = 0;

50             int r1 = MaximalSublistMatch_Slow(list1, start1, count1, list2, start2 + 1, count2 - 1, comparer, temp11, temp12);

51             if (r1 < std::min(count1 - 1, count2))

52             {

53                 r2 = MaximalSublistMatch_Slow(list1, start1 + 1, count1 - 1, list2, start2, count2, comparer, temp21, temp22);

54             }

55             if (r1 < r2)

56             {

57                 for (int i = 0; i < r2; i++)

58                 {

59                     indices1.Add(temp21[i]);

60                     indices2.Add(temp22[i]);

61                 }

62                 return r2;

63             }

64             else

65             {

66                 for (int i = 0; i < r1; i++)

67                 {

68                     indices1.Add(temp11[i]);

69                     indices2.Add(temp12[i]);

70                 }

71                 return r1;

72             }

73         }

74     }

75 }

 

A simpler version naive alternative (not equivalent, but ok for most use; and minor change to it can improve accuracy not so sure of what significance this method can be, with a fast optimum approach found available) is

It's equivalent is,

 1 // this is a version of maximal match with a complexity of O(N)

 2 static int MaximalMatch(TListRef list1, int start1, int count1, TListRef list2, int start2, int count2, 

 3     const IEqualityComparer<T> &comparer, List<int> &indices1, List<int> &indices2)

 4 {

 5     int matchStart2 = start2;

 6     for (int i1 = start1; i1 < start1 + count1; i1++)

 7     {

 8         const T &v1 = list1[i1];

 9         for (int i2 = matchStart2; i2 < start2 + count2; i2++)

10         {

11             const T &v2 = list2[i2];

12             if (comparer.Equals(v1,v2))

13             {

14                 indices1.Add(i1);

15                 indices2.Add(i2);

16                 matchStart2 = i2+1;

17                 break;

18             }

19         }

20     }

21     return indices1.GetCount();

22 }

Of course this is is epically faster, simpler and less error-prone than the previous one. but it doesn't provide the optimal result.
You can imagine how an application would suffer from the exp(N)-complexity shit.

The fast equivalent should be using dynamic programming and go as follows

(The standard C# version has been updated to the QSharp library at https://qsharp.codeplex.com/SourceControl/latest#QSharp/QSharp.Scheme.Classical.Sequential/MaxSublistMatch.cs)

  1 struct MaxMatchDPResult

  2 {

  3     bool Done;

  4     List<int> Indices1;

  5     List<int> Indices2;

  6 };

  7 

  8 

  9 // FB: 6462

 10 // NOTE this is a version of maximal match using dynamic programming

 11 //      it has a time complexity of around O(N*N) and space complexity of about O(N^4)

 12 //      This is a recommended version as it provides optimal result and is fast

 13 static int MaximalSublistMatch_DP(TListRef list1, int start1, int count1, TListRef list2, int start2, int count2,

 14     const IEqualityComparer<T> &comparer, List<int> &indices1, List<int> &indices2)

 15 {

 16     int maxSofar = 0;

 17     std::vector<std::vector<MaxMatchDPResult>> map;

 18     for (int i = 0; i < count1+1; i++)

 19     {

 20         map.push_back(std::vector<MaxMatchDPResult>());

 21         for (int j = 0; j < count2+1; j++)

 22         {

 23             map[i].push_back(MaxMatchDPResult());

 24             map[i][j].Done = false;

 25         }

 26     }

 27     return MaximalSublistMatch_DP(list1, start1, count1, list2, start2, count2, comparer, indices1, indices2, map);

 28 }

 29 

 30 static int MaximalSublistMatch_DP_Lookup(TListRef list1, int start1, int count1, TListRef list2, int start2, int count2,

 31     const IEqualityComparer<T> &comparer, List<int> &indices1, List<int> &indices2, std::vector <std::vector<MaxMatchDPResult>> &map)

 32 {

 33     if (count1 <= 0 || count2 <= 0)

 34     {

 35         return 0;

 36     }

 37     const MaxMatchDPResult &result = map[count1][count2];

 38     int r;

 39     if (result.Done)

 40     {

 41         r = result.Indices1.GetCount();

 42         for (int i = 0; i < r; i++)

 43         {

 44             indices1.Add(result.Indices1[i]);

 45             indices2.Add(result.Indices2[i]);

 46         }

 47     }

 48     else

 49     {

 50         List<int> tempIndices1, tempIndices2;

 51         r = MaximalSublistMatch_DP(list1, start1, count1, list2, start2, count2, comparer, tempIndices1, tempIndices2, map);

 52         map[count1][count2].Done = true;

 53         map[count1][count2].Indices1 = tempIndices1;

 54         map[count1][count2].Indices2 = tempIndices2;

 55         for (int i = 0; i < r; i++)

 56         {

 57             indices1.Add(tempIndices1[i]);

 58             indices2.Add(tempIndices2[i]);

 59         }

 60     }

 61     return r;

 62 }

 63 

 64 static int MaximalSublistMatch_DP(TListRef list1, int start1, int count1, TListRef list2, int start2, int count2,

 65     const IEqualityComparer<T> &comparer, List<int> &indices1, List<int> &indices2, std::vector<std::vector<MaxMatchDPResult>> &map)

 66 {

 67     bool eq = comparer.Equals(list1[start1], list2[start2]);

 68     if (eq)

 69     {

 70         indices1.Add(start1);

 71         indices2.Add(start2);

 72         int r = MaximalSublistMatch_DP_Lookup(list1, start1 + 1, count1 - 1, list2, start2 + 1, count2 - 1, comparer, indices1, indices2, map) + 1;

 73         return r;

 74     }

 75 

 76     List<int> temp11, temp12, temp21, temp22;

 77     int r2 = 0;

 78     int r1 = MaximalSublistMatch_DP_Lookup(list1, start1, count1, list2, start2 + 1, count2 - 1, comparer, temp11, temp12, map);

 79     if (r1 < 

 80 #if defined(min)

 81         min(count1 - 1, count2)

 82 #else

 83         std::min(count1 - 1, count2)

 84 #endif

 85         )

 86     {

 87         r2 = MaximalSublistMatch_DP_Lookup(list1, start1 + 1, count1 - 1, list2, start2, count2, comparer, temp21, temp22, map);

 88     }

 89     if (r2 > r1)

 90     {

 91         for (int i = 0; i < r2; i++)

 92         {

 93             indices1.Add(temp21[i]);

 94             indices2.Add(temp22[i]);

 95         }

 96         return r2;

 97     }

 98     else

 99     {

100         for (int i = 0; i < r1; i++)

101         {

102             indices1.Add(temp11[i]);

103             indices2.Add(temp12[i]);

104         }

105         return r1;

106     }

107 }

 

This was stupid, but classic!

 

你可能感兴趣的:(life)