[Algorithm]随机抖动整数拆分

测试效果:
当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);
        }
    }
}

如果有什么更好的整数拆分算法,望留言。

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值