2015-12-15 50 views
8

我想爲特定問題開發自我訓練算法。爲了簡單起見,我會把它歸結爲一個簡單的例子。自我訓練算法

更新:我添加了一個工作解決方案,以回答下面的這個問題。

比方說,我有一個來自數據庫的巨大實體列表。每個實體都是相同的類型,並有4個字節類型的屬性。

public class Entity 
{ 
    public byte Prop1 { get; set; } 
    public byte Prop2 { get; set; } 
    public byte Prop3 { get; set; } 
    public byte Prop4 { get; set; } 
} 

現在我想動態測試每個實體的一個或多個屬性對一個簡單的條件。這基本上意味着我想測試所有屬性對這種情況的所有可能的組合。

爲了完成這項工作,我爲屬性創建了一個位掩碼。

[Flags] 
public enum EEntityValues 
{ 
    Undefined = 0, 
    Prop1 = 1, 
    Prop2 = 2, 
    Prop3 = 4, 
    Prop4 = 8, 
} 

並添加了一個方法來獲取位掩碼的最大值。這個例子返回15(1 + 2 + 4 + 8)。

public static int GetMaxValue<T>() where T : struct 
{ 
    return Enum.GetValues(typeof(T)).Cast<int>().Sum(); 
} 

在這個階段,我可以用一個簡單的循環迭代所有的屬性組合。例如,在第一次迭代中,測試屬性Prop1,在第二次迭代中測試Prop2,在第三次迭代中測試Prop1和Prop2等等。

for(int i = 1; i <= GetMaxValue<EEntityValues>(); i++) 
{ 
    EEntityValues flags = (EEntityValues)i; 

    if(flags.HasSet(EEntityValues.Prop1)) 
    { 
     .... 
    } 
} 

現在讓我們將實體放入遊戲中。

List<Entity> entities = GetEntitiesFromDb(); 

for(int i = 1; i <= GetMaxValue<EEntityValues>(); i++) 
{ 
    EEntityValues flags = (EEntityValues)i; 
    byte minProp1Value = 10, minProp2Value = 20, minProp3Value = 30, minProp4Value = 40; 

    foreach(Entitiy entity in entities) 
    { 
     if(flags.HasSet(EEntityValues.Prop1) && entitiy.Prop1 >= minProp1Value) 
     { 
       .... 
     } else { continue; } 

     if(flags.HasSet(EEntityValues.Prop2) && entitiy.Prop2 >= minProp2Value) 
     { 
       .... 
     } else { continue; } 
    } 
} 

那麼,如果我的最小值是靜態的,這個效果很好。

現在讓我們變得更加複雜。我們記得,在第一次迭代時,我們只測試屬性Prop1,因爲位掩碼是1. Prop1的值範圍是0..255。我還爲此屬性定義了一個有效範圍爲1..255的最小值。這個最小值只是一個過濾器,用於跳過foreach循環中的實體。

現在我想測試屬性Prop1,而我升了最低水平。這些測試不是問題的一部分,所以我不包括他們到我的樣品。

 byte minProp1Value = 1; 

    while(minProp1Value <= 255) 
    { 
     foreach(Entitiy entity in entities) 
     { 
       if(flags.HasSet(EEntityValues.Prop1) && entitiy.Prop1 >= minProp1Value) 
       { 
        .... // Testing the matching entity and storing the result 
       } else { continue; } 
     } 

     minProp1Value++; 
    } 

這對一個屬性很簡單。在第三次迭代我必須處理2個屬性,PROP1和PROP2,因爲位掩碼是3

 byte minProp1Value = 1, minProp2Value = 1; 

    while(minProp1Value <= 255) 
    { 
     while(minProp2Value <= 255) 
     { 
       foreach(Entitiy entity in entities) 
       { 
        .... 
       } 

       minProp2Value++; 
     } 

     minProp1Value++; 
     minProp2Value = 1; 
    } 

正如你所看到的,在這個階段,我反對測試每個實體的PROP1和PROP2上升的最低水平。

因爲我正在處理動態生成的多個屬性集,所以我無法將while循環硬編碼到我的代碼中。這就是爲什麼我正在尋找一個更智能的解決方案來測試給定屬性集(位掩碼)的所有可能的最小值組合。

+0

不確定我關注..你是說你想要一種方法來測試(例如)prop1和prop2而不是所有4的組合嗎? – Rob

+1

你也意識到這四個屬性有'4,228,250,625'組合嗎? – Rob

+0

所以你的意思是你希望能夠檢查'prop1&prop2'組合,而且'prop1&prop3',(等等)以及所有的一次? – Rob

回答

1

休息一下後,我想出了一個解決方案,似乎符合我的要求。限制是所有被測試的屬性都應該是相同的值類型,對於我來說這對我來說很好,因爲所有的屬性都是抽象的百分比值。

順便說一下,我不確定主題「自我訓練算法」是否有點誤導。實現這種解決方案有幾種方法,但如果您不知道數據的行爲方式以及這些值的效果如何,最簡單的解決方案就是強制所有可能的組合來確定最佳擬合結果。這就是我在這裏展示的。

不管怎樣,爲了測試目的,我在實體類中添加了一個隨機數生成器。

public class Entity 
{ 
    public byte Prop1 { get; set; } 
    public byte Prop2 { get; set; } 
    public byte Prop3 { get; set; } 
    public byte Prop4 { get; set; } 

    public Entity() 
    { 
     Random random = new Random(Guid.NewGuid().GetHashCode()); 
     byte[] bytes = new byte[ 4 ]; 

     random.NextBytes(bytes); 

     this.Prop1 = bytes[0]; 
     this.Prop2 = bytes[1]; 
     this.Prop3 = bytes[2]; 
     this.Prop4 = bytes[3]; 
    } 
} 

我的位掩碼保持不變。

[Flags] 
public enum EProperty 
{ 
    Undefined = 0, 
    Prop1 = 1, 
    Prop2 = 1 << 1, 
    Prop3 = 1 << 2, 
    Prop4 = 1 << 3 
} 

比我添加了一些新的擴展方法來處理我的位掩碼。

public static class BitMask 
{ 
    public static int GetMaxValue<T>() where T : struct 
    { 
     return Enum.GetValues(typeof (T)).Cast<int>().Sum(); 
    } 

    public static int GetTotalCount<T>() where T : struct 
    { 
     return Enum.GetValues(typeof (T)).Cast<int>().Count(e => e > 0); 
    } 

    public static int GetFlagCount<T>(this T mask) where T : struct 
    { 
     int result = 0, value = (int) (object) mask; 

     while (value != 0) 
     { 
      value = value & (value - 1); 
      result++; 
     } 

     return result; 
    } 

    public static IEnumerable<T> Split<T>(this T mask) 
    { 
     int maskValue = (int) (object) mask; 

     foreach (T flag in Enum.GetValues(typeof (T))) 
     { 
      int flagValue = (int) (object) flag; 

      if (0 != (flagValue & maskValue)) 
      { 
       yield return flag; 
      } 
     } 
    } 
} 

比我寫了一個查詢構建器

public static class QueryBuilder 
{ 
    public static IEnumerable<Entity> Where(this IEnumerable<Entity> entities, EProperty[] properties, int[] values) 
    { 
     IEnumerable<Entity> result = entities.Select(e => e); 

     for (int index = 0; index <= properties.Length - 1; index++) 
     { 
      EProperty property = properties[index]; 
      int value = values[index]; 

      switch (property) 
      { 
       case EProperty.Prop1: 
        result = result.Where(e => Math.Abs(e.Prop1) >= value); 
        break; 
       case EProperty.Prop2: 
        result = result.Where(e => Math.Abs(e.Prop2) >= value); 
        break; 
       case EProperty.Prop3: 
        result = result.Where(e => Math.Abs(e.Prop3) >= value); 
        break;    
       case EProperty.Prop4: 
        result = result.Where(e => Math.Abs(e.Prop4) >= value); 
        break; 
      } 
     } 

     return result; 
    } 
} 

,最後我已經準備好進行訓練。

private const int maxThreads = 10; 

    private const int minValue = 0; 
    private const int maxValue = 100; 

    private static IEnumerable<Entity> entities; 

    public static void Main(string[] args) 
    { 
     Console.WriteLine(DateTime.Now.ToLongTimeString()); 

     entities = Enumerable.Repeat(new Entity(), 10).ToList(); 

     Action<EProperty[], int[]> testCase = RunTestCase; 
     RunSelfTraining(testCase); 

     Console.WriteLine(DateTime.Now.ToLongTimeString()); 
     Console.WriteLine("Done."); 

     Console.Read(); 
    } 

    private static void RunTestCase(EProperty[] properties, int[] values) 
    {   
     foreach(Entity entity in entities.Where(properties, values)) 
     { 

     } 
    } 

    private static void RunSelfTraining<T>(Action<T[], int[]> testCase) where T : struct 
    { 
     ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = maxThreads }; 

     for (int maskValue = 1; maskValue <= BitMask.GetMaxValue<T>(); maskValue++) 
     { 
      T mask = (T) (object)maskValue; 
      T[] properties = mask.Split().ToArray();   

      int variations = (int) Math.Pow(maxValue - minValue + 1, properties.Length); 

      Parallel.For(1, variations, parallelOptions, variation => 
      { 
       int[] values = GetVariation(variation, minValue, maxValue, properties.Length).ToArray(); 
       testCase.Invoke(properties, values);   
      }); 
     } 
    } 

    public static IEnumerable<int> GetVariation(int index, int minValue, int maxValue, int count) 
    { 
     index = index - 1; 
     int range = maxValue - minValue + 1; 

     for(int j = 0; j < count; j++) 
     { 
      yield return index % range + minValue; 
      index = index/range; 
     } 
    } 
}