110 lines
2.8 KiB
C#
110 lines
2.8 KiB
C#
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 洗牌算法
|
||
|
||
/// <summary>
|
||
/// Fisher-Yates洗牌算法,时间复杂度为O(n*n),空间复杂度为O(n)
|
||
/// </summary>
|
||
/// <param name="length">待选项目的索引长度</param>
|
||
/// <param name="select">输出项目的索引长度</param>
|
||
/// <returns></returns>
|
||
public static List<int> FisherYatesShuffle(int length, int select)
|
||
{
|
||
if(select > length)
|
||
throw new ArgumentOutOfRangeException("在进行洗牌时,要求输出的项目比拥有的项目更多,这是不合理的。");
|
||
|
||
var random = new System.Random();
|
||
|
||
List<int> arr = Enumerable.Range(0, length).ToList();
|
||
List<int> res = new List<int>();
|
||
|
||
for(int i = 0; i < select; ++i)
|
||
{
|
||
int k = random.Next(arr.Count);
|
||
res.Add(arr[k]);
|
||
arr.RemoveAt(k);
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 蓄水池抽样算法,可以在线程安全的情况下完成洗牌。
|
||
/// </summary>
|
||
/// <param name="arr"></param>
|
||
/// <param name="select">需要抽取的样本 M</param>
|
||
/// <param name="reservoir"></param>
|
||
/// <param name="cancellationToken"></param>
|
||
public static void ReservoirSampling(List<int> arr, int select, ConcurrentBag<int> reservoir, CancellationToken cancellationToken)
|
||
{
|
||
/**
|
||
* int M = 3; // 需要抽样的元素个数
|
||
* int totalElements = 10; // 输入数据的总元素个数
|
||
* var arr = new List<int>();
|
||
*
|
||
* for (int i = 1; i <= totalElements; i++)
|
||
* {
|
||
* arr.Add(i);
|
||
* }
|
||
*
|
||
* ConcurrentBag<int> reservoir = new ConcurrentBag<int>();
|
||
* 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 应用算法
|
||
|
||
/// <summary>
|
||
/// 获取列表中随机顺序的对象
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="list"></param>
|
||
/// <param name="count">需要采样的数量</param>
|
||
/// <returns></returns>
|
||
public static IEnumerable<T> GetRandom<T>(this List<T> 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
|
||
}
|
||
} |