测试效果:
当number=101、size=6、wave=3时,可得到如下效果:
- 17、19、16、18、13、18
- 18、15、18、14、19、17
可看到生成的随机数在16上下浮动,幅度不超过3,且总和为101。
源码如下:
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
namespace XM.Tool
{
public class MathCalc
{
/// <summary>
/// 随机抖动整数拆分
/// </summary>
/// <param name="number">需要分割的整数</param>
/// <param name="size">份数</param>
/// <param name="wave">抖动半径</param>
/// <returns></returns>
public static int[] RandomSegmentation(int number, int size, int wave)
{
int midNumber = number / size - wave;//抖动分界线
//check
if (midNumber < 0)
{
return null;
}
int wavex2 = wave * 2;
int[] numbers = new int[size];
int valueCnt = number % size + size * wave;//需要随机分配的值
int tmpRand;
int tmpMin;
int tmpMax;
int tmpDelta;
for (int i = 0; i < size; i++)
{
//随机一个值
tmpRand = Random.Range(0, wavex2 + 1);
numbers[i] = tmpRand;
//总量减少
valueCnt -= tmpRand;
}
//分配剩余总量
if (valueCnt != 0)
{
if (valueCnt > 0)
{//分配剩余总量
//需要增加的值的范围
tmpMin = 0;
tmpMax = wavex2 - 1;
//增量
tmpDelta = 1;
}
else
{//分配多了,需要减少
//需要减少的值的范围
tmpMin = 1;
tmpMax = wavex2;
//增量
tmpDelta = -1;
}
//递归随机分配
FindNumberToRandomSet(numbers, tmpMin, tmpMax, ref valueCnt, tmpDelta);
}
if (midNumber != 0)
{
for (int i = 0; i < size; i++)
{
numbers[i] += midNumber;
}
}
return numbers;
}
/// <summary>
/// 用于随机填充数组
/// </summary>
/// <param name="numbers">需要填充的数组</param>
/// <param name="min">需要填充的最小值</param>
/// <param name="max">需要填充的最大值</param>
/// <param name="valueCnt">可用填充值</param>
/// <param name="delta">填充步长,1或-1</param>
/// <param name="useIndexLst">不填</param>
/// <returns></returns>
protected static bool FindNumberToRandomSet(int[] numbers, int min, int max, ref int valueCnt, int delta, List<int> useIndexLst = null/*don`t need set*/)
{
//当可用值为0时,即分配完成,结束递归
if (valueCnt == 0)
{//over
return true;
}
if (useIndexLst == null)
{//init
useIndexLst = new List<int>();
//获取符合要求的元素下标
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] >= min && numbers[i] <= max)
{
useIndexLst.Add(i);
}
}
}
Debug.Assert(useIndexLst.Count != 0, "useIndexLst.Count is 0 !!!!!!");
int randUseIndex = Random.Range(0, useIndexLst.Count);//随机一个需要修改的元素下标
int index = useIndexLst[randUseIndex];//得到下标
useIndexLst.RemoveAt(randUseIndex);//移除下标
//修改值
numbers[index] += delta;
//总量减少
valueCnt -= delta;
if (numbers[index] >= min && numbers[index] <= max)
{//判断该元素是否可以继续修改,可以的话将下标存入列表
useIndexLst.Add(index);
}
//递归
return FindNumberToRandomSet(numbers, min, max, ref valueCnt, delta, useIndexLst);
}
}
}
如果有什么更好的整数拆分算法,望留言。