using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;
namespace XericLibrary.Runtime.MacroLibrary
{
public static partial class MacroMath
{
#region 洗牌算法
///
/// Fisher-Yates洗牌算法,时间复杂度为O(n*n),空间复杂度为O(n)
///
/// 待选项目的索引长度
/// 输出项目的索引长度
///
public static List FisherYatesShuffle(int length, int select)
{
if(select > length)
throw new ArgumentOutOfRangeException("在进行洗牌时,要求输出的项目比拥有的项目更多,这是不合理的。");
var random = new System.Random();
List arr = Enumerable.Range(0, length).ToList();
List res = new List();
for(int i = 0; i < select; ++i)
{
int k = random.Next(arr.Count);
res.Add(arr[k]);
arr.RemoveAt(k);
}
return res;
}
///
/// 蓄水池抽样算法,可以在线程安全的情况下完成洗牌。
///
///
/// 需要抽取的样本 M
///
///
public static void ReservoirSampling(List arr, int select, ConcurrentBag reservoir, CancellationToken cancellationToken)
{
/**
* int M = 3; // 需要抽样的元素个数
* int totalElements = 10; // 输入数据的总元素个数
* var arr = new List();
*
* for (int i = 1; i <= totalElements; i++)
* {
* arr.Add(i);
* }
*
* ConcurrentBag reservoir = new ConcurrentBag();
* Random random = new Random();
*
* var cancellationTokenSource = new CancellationTokenSource();
* var cancellationToken = cancellationTokenSource.Token;
*
* ThreadPool.QueueUserWorkItem(state =>
* ReservoirSampling(arr, M, reservoir, random, cancellationToken);
* , null);
*
* // 停止抽样
* cancellationTokenSource.Cancel();
*/
var random = new System.Random();
for(int i = select; i < arr.Count; ++i)
{
if(cancellationToken.IsCancellationRequested)
return;
int k = random.Next(i + 1); // 生成一个随机数 k,范围是 [0, i]
if(k < select)
{
// 如果 k 小于 M,将 arr[k] 添加到抽样结果中
reservoir.Add(arr[k]);
}
}
}
#endregion
#region 应用算法
///
/// 获取列表中随机顺序的对象
///
///
///
/// 需要采样的数量
///
public static IEnumerable GetRandom(this List list, int count = -1)
{
var index = FisherYatesShuffle(list.Count, count <= 0 ? list.Count : count);
for(int i = 0; i < index.Count; i++)
yield return list[index[i]];
}
#endregion
}
}