typescript根据权重进行随机

2020-11-24 18:07:10 浏览数 (1)

前言: 这是我在去年记录的笔记, 那会儿在上一家公司, 以nodejs为环境来开发小游戏, 使用ts来编写. 那会儿写笔记只顾着记录, 并没着重去组织语言, 我现在也是搬运过来, 并未更改正文, 望见谅.

正文

代码如下

代码语言:javascript复制
/**
 * describe: 根据权重来随机
 * 从一个数组中进行随机选择元素, 需要其元素为一个obj类型, 包含名为weight的key
 * 返回下标
 * @param array 
 */
function randByWeight(array: Array<Object>): number {
    let self = this;
    let totalWeight: number = 0;
    let randIndex: number;
    array.forEach(element => {
        totalWeight  = element["weight"];
    });

    if (totalWeight <= 0) {
        randIndex = undefined;
        return randIndex
    } else {
        let randVal: number = rand(1, totalWeight);
        for (let index = 0; index < array.length; index  ) {
            const element = array[index];
            if (randVal <= element["weight"]) {
                randIndex = index;
                break;
            } else {
                randVal -= element["weight"];
            }
        }
    }
    return randIndex;
}

function rand(min: number, max: number): number {
    let n: number = max - min;
    return min   Math.round(Math.random() * n);
}

测试传入参数如下

代码语言:javascript复制
let weightObjArr: Array<any> = [{"weight": 50, "name": 'attack'}, {"weight": 50, "name": 'defense'}, {"weight": 50, "name": 'balanced'}];

update-1

代码如下

代码语言:javascript复制
interface WeightMsg{
    id: number,
    weight: number
}

/**
 * describe: 根据权重来随机
 * 从一个数组中进行随机选择元素, 需要其元素为一个obj类型, 包含名为weight的key
 * 返回下标
 * @param array 
 */
function randByWeight(arr: Array<WeightMsg>): number {
    let totalWeight: number = 0;
    let randIndex: number;
    for(let itemWeightMsg of arr){
        totalWeight  = itemWeightMsg.weight;
    }

    if (totalWeight <= 0) {
        return randIndex
    } else {
        let randVal: number = rand(1, totalWeight);
        for (let index = 0; index < arr.length; index  ) {
            if (randVal <= arr[index].weight) {
                randIndex = index;
                break;
            } else {
                randVal -= arr[index].weight;
            }
        }
    }
    return randIndex;
}

function rand(min: number, max: number): number {
    let n: number = max - min;
    return min   Math.round(Math.random() * n);
}

/**
 * describe: 将数组结构的权重数据转成带weightd的key的obj元素类型的数组
 * 如arr: [[1, 10]], 1为id, 10表示权重
 * @param arr: 双重数组, 且元素长度为2, 元素个数不限, 子元素为number
 */
function arrChangeWeightObjArr(arr: Array<Array<number>>): Array<WeightMsg> {
    let arrWeight: Array<WeightMsg> = [];
    for (let arrItem of arr) {
        let itemObj: WeightMsg = {
            id: arrItem[0],
            weight: arrItem[1]
        }
        arrWeight.push(itemObj);
    }
    return arrWeight;
}

/**
 * describe: 权重抽取id
 * @param arr 
 */
function getIdByWeght(arr: Array<Array<number>>): number{
    let weightArr: Array<WeightMsg> = arrChangeWeightObjArr(arr);
    let resIndex: number = randByWeight(weightArr);
    return weightArr[resIndex].id;
}

/**
 * describe: 记录次数, 将添加值为value的个数信息计入到containerObj中
 * @param containerObj : 存放数据的obj
 * @param value : 记录的值
 * @param num 
 */
function statisticalNum(containerObj: Object, value: number, num: number = 1): Object{
    let oldNum: number = containerObj[value] || 0;
    let newNum: number = oldNum   num;
    containerObj[value] = newNum;
    return containerObj;
}

let testTypeArr: Array<Array<number>> = [[1, 10], [2, 10]];
let resObj: Object = {}

for(let intIndex = 0; intIndex < 10; intIndex   ){
    let typeNum: number = getIdByWeght(testTypeArr);
    resObj = statisticalNum(resObj, typeNum)
}

console.log('resObj: ', resObj);

如上面的输出如下

代码语言:javascript复制
PS D:new-companyworknodejsvscodedoNewTest190305newTest> tsc .newTest.ts
PS D:new-companyworknodejsvscodedoNewTest190305newTest> node .newTest.js
resObj:  { '1': 7, '2': 3 }

感觉有点差距

如果我把参数代码改成如下

代码语言:javascript复制
let testTypeArr: Array<Array<number>> = [[1, 10], [2, 10], [3, 10], [4, 10], [5, 10], [6, 10], [7, 10], [8, 10], [9, 10], [10, 10]];
let resObj: Object = {}

for(let intIndex = 0; intIndex < 100000; intIndex   ){
    let typeNum: number = getIdByWeght(testTypeArr);
    resObj = statisticalNum(resObj, typeNum)
}

console.log('resObj: ', resObj);

其输出如

代码语言:javascript复制
PS D:new-companyworknodejsvscodedoNewTest190305newTest> tsc .newTest.ts
PS D:new-companyworknodejsvscodedoNewTest190305newTest> node .newTest.js
resObj:  { '1': 9659,
  '2': 10241,
  '3': 10086,
  '4': 9928,
  '5': 9985,
  '6': 10077,
  '7': 10263,
  '8': 10064,
  '9': 10127,
  '10': 9570 }
PS D:new-companyworknodejsvscodedoNewTest190305newTest>

运行次数多了, 会发现第一个和最后一个随机出来的次数总会少那么一些

还得改进

后来发现是这个rand函数的原因, 于是重新写了个函数, 如

代码语言:javascript复制
function getRandomIntInclusive(min: number, max: number): number {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min   1))   min; //The maximum is inclusive and the minimum is inclusive 
}

整理后, 代码如下

代码语言:javascript复制
interface WeightMsg{
    id?: number,
    weight: number
}

/**
 * describe: 根据权重来随机
 * 从一个数组中进行随机选择元素, 需要其元素为一个obj类型, 包含名为weight的key
 * 返回下标
 * @param arr 
 */
function randByWeight(arr: Array<WeightMsg>): number {
    let totalWeight: number = 0;
    let randIndex: number;
    for(let itemWeightMsg of arr){
        totalWeight  = itemWeightMsg.weight;
    }

    if (totalWeight <= 0) {
        return randIndex
    } else {
        let randVal: number = getRandomIntInclusive(1, totalWeight);
        for (let index = 0; index < arr.length; index  ) {
            if (randVal <= arr[index].weight) {
                randIndex = index;
                break;
            } else {
                randVal -= arr[index].weight;
            }
        }
    }
    return randIndex;
}

/**
 * describe: 在范围内获取随机整数值 [min, max]
 * @param min : 最小值
 * @param max : 最大值
 */
function getRandomIntInclusive(min: number, max: number): number {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min   1))   min; //The maximum is inclusive and the minimum is inclusive 
}

/**
 * describe: 将数组结构的权重数据转成带weightd的key的obj元素类型的数组
 * 如arr: [[1, 10]], 1为id, 10表示权重
 * @param arr: 双重数组, 且元素长度为2, 元素个数不限, 子元素为number
 */
function arrChangeWeightObjArr(arr: Array<Array<number>>): Array<WeightMsg> {
    let arrWeight: Array<WeightMsg> = [];
    for (let arrItem of arr) {
        let itemObj: WeightMsg = {
            id: arrItem[0],
            weight: arrItem[1]
        }
        arrWeight.push(itemObj);
    }
    return arrWeight;
}

/**
 * describe: 权重抽取id
 * @param arr 
 */
function getIdByWeght(arr: Array<Array<number>>): number{
    let weightArr: Array<WeightMsg> = arrChangeWeightObjArr(arr);
    let resIndex: number = randByWeight(weightArr);
    return weightArr[resIndex].id;
}

/**
 * describe: 记录次数, 将添加值为value的个数信息计入到containerObj中
 * @param containerObj : 存放数据的obj
 * @param value : 记录的值
 * @param num 
 */
function statisticalNum(containerObj: Object, value: number, num: number = 1): Object{
    let oldNum: number = containerObj[value] || 0;
    let newNum: number = oldNum   num;
    containerObj[value] = newNum;
    return containerObj;
}

let testTypeArr: Array<Array<number>> = [[1, 10], [2, 10], [3, 10], [4, 10], [5, 10], [6, 10], [7, 10], [8, 10], [9, 10], [10, 10]];
let resObj: Object = {}

for(let intIndex = 0; intIndex < 100000; intIndex   ){
    let typeNum: number = getIdByWeght(testTypeArr);
    resObj = statisticalNum(resObj, typeNum)
}

console.log('resObj: ', resObj);

运行输出如下

代码语言:javascript复制
PS D:new-companyworknodejsvscodedoNewTest190305newTest> tsc .newTest.ts
PS D:new-companyworknodejsvscodedoNewTest190305newTest> node .newTest.js
resObj:  { '1': 10021,
  '2': 10010,
  '3': 10071,
  '4': 9943,
  '5': 10026,
  '6': 9970,
  '7': 9919,
  '8': 10078,
  '9': 9963,
  '10': 9999 }


文章首发来自公众号:程序员品

欢迎关注欢迎关注

0 人点赞