自训练算法
Self Training Algorithm
我想针对特定问题开发一种自训练算法。为了简单起见,我将把它归结为简单的例子。
更新:我添加了一个有效的解决方案作为下面这个问题的答案。
假设我有大量来自数据库的实体列表。每个实体都是同一类型,有4个byte类型的属性。
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,
}
并添加了获取位掩码最大值的方法。其中 returns 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 循环硬编码到我的代码中。这就是为什么我正在寻找一种更智能的解决方案来测试给定 属性-set(位掩码)的最小值的所有可能组合。
休息后,我想出了一个似乎符合我要求的解决方案。限制是所有测试的属性应该是具有相同值范围的相同类型,这对我来说很好,因为所有属性都是抽象的百分比值。
顺便说一句,我不确定 "self training algorithm" 这个话题在这里是否有点误导。有几种方法可以实现这样的解决方案,但如果您不知道数据的行为方式以及值的影响,最简单的解决方案是强制所有可能的组合以确定最佳拟合结果。这就是我在这里展示的内容。
无论如何,出于测试目的,我向我的实体添加了一个随机数生成器 class。
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;
}
}
}
我想针对特定问题开发一种自训练算法。为了简单起见,我将把它归结为简单的例子。
更新:我添加了一个有效的解决方案作为下面这个问题的答案。
假设我有大量来自数据库的实体列表。每个实体都是同一类型,有4个byte类型的属性。
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,
}
并添加了获取位掩码最大值的方法。其中 returns 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 循环硬编码到我的代码中。这就是为什么我正在寻找一种更智能的解决方案来测试给定 属性-set(位掩码)的最小值的所有可能组合。
休息后,我想出了一个似乎符合我要求的解决方案。限制是所有测试的属性应该是具有相同值范围的相同类型,这对我来说很好,因为所有属性都是抽象的百分比值。
顺便说一句,我不确定 "self training algorithm" 这个话题在这里是否有点误导。有几种方法可以实现这样的解决方案,但如果您不知道数据的行为方式以及值的影响,最简单的解决方案是强制所有可能的组合以确定最佳拟合结果。这就是我在这里展示的内容。
无论如何,出于测试目的,我向我的实体添加了一个随机数生成器 class。
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;
}
}
}