using Island.StandardLib.Storage; using System; namespace Island.StandardLib.Math { /// /// 表示一个总和为1的平衡容器 /// public class Percentage : IStorable { StorableFixedArray percentages; public int KeyLength => percentages.Length; public float Maxium { get { float maxium = percentages[0].Value; for (int i = 1; i < percentages.Count; i++) if (percentages[i].Value > maxium) maxium = percentages[i].Value; return maxium; } } public int MaxiumId { get { float maxium = percentages[0].Value; int id = 0; for (int i = 1; i < percentages.Count; i++) if (percentages[i].Value > maxium) { maxium = percentages[i].Value; id = i; } return id; } } public float Minium { get { float minium = percentages[0].Value; for (int i = 1; i < percentages.Count; i++) if (percentages[i].Value < minium) minium = percentages[i].Value; return minium; } } public int MiniumId { get { float minium = percentages[0].Value; int id = 0; for (int i = 1; i < percentages.Count; i++) if (percentages[i].Value < minium) { minium = percentages[i].Value; id = i; } return id; } } public Percentage() { } /// /// 初始化容器 /// /// 初始数据 public Percentage(params float[] sourceValue) { percentages = new StorableFixedArray(); if (sourceValue.Length == 0) throw new Exception(); float val = 0; for (int i = 0; i < sourceValue.Length; i++) { val += sourceValue[i]; percentages.Add(new Key(sourceValue[i])); } if (val != 1f) AdjustBalance(); } /// /// 获取和设置数据。其中,设置数据后会自动按比例平衡容器,保证容器总和为1 /// /// 数据序号 /// public float this[int index] { get => percentages[index].Value; set => AdjustKeep(index, value); } public float[] SelectBalance(params int[] selectedIndex) { float[] values = new float[selectedIndex.Length]; for (int i = 0; i < selectedIndex.Length; i++) values[i] = percentages[selectedIndex[i]].Value; float total = 0f; for (int i = 0; i < values.Length; i++) total += values[i]; float px = 1 / total; for (int i = 0; i < values.Length; i++) values[i] *= px; return values; } public int Random(int seed = 0) { Random rd = seed == 0 ? new Random() : new Random(seed); double select = rd.NextDouble(); float thisMin = 0f; for (int i = 0; i < KeyLength; i++) { float thisMax = thisMin + this[i]; if (select >= thisMin && select < thisMax) return i; thisMin = thisMax; } return -1; } public long Total(int index, long totalValue) { return (long)(this[index] * totalValue); } void AdjustBalance() { float total = 0f; for (int i = 0; i < KeyLength; i++) total += this[i]; float px = 1 / total; for (int i = 0; i < KeyLength; i++) percentages[i].Value *= px; } void AdjustKeep(int keepIndex, float newValue) { AdjustBalance(); float distance = this[keepIndex] - newValue; percentages[keepIndex].Value = newValue; float[] gains = new float[KeyLength - 1]; int g = 0; for (int i = 0; i < KeyLength; i++) { if (keepIndex == i) continue; gains[g] = this[i]; g++; } Percentage p = new Percentage(gains); g = 0; for (int i = 0; i < KeyLength; i++) { if (keepIndex == i) continue; percentages[i].Value += p[g] * distance; g++; } } public override string ToString() { string s = "Percentage ["; float total = 0f; for (int i = 0; i < KeyLength; i++) { total += this[i]; if (i == KeyLength - 1) s += this[i].ToString("P"); else s += this[i].ToString("P") + ", "; } return s + "]"; } public void ReadFromData(DataStorage data) { data.Read(out percentages); } public void WriteToData(DataStorage data) { data.Write(percentages); } class Key : IStorable { public float Value; public Key() => Value = 0f; public Key(float sourceValue) => Value = sourceValue; public void ReadFromData(DataStorage data) => data.Read(out Value); public void WriteToData(DataStorage data) => data.Write(Value); } } }