This repository has been archived on 2025-09-23. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
XericLibrary-OLD/Runtime/MicroLibrary/MacroList.cs
T

171 lines
4.3 KiB
C#

using Codice.CM.Triggers;
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);
/// 缺点是元素0很大概率会在最后出现
/// </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="length"></param>
/// <param name="select"></param>
/// <param name="repet"></param>
/// <returns></returns>
public static List<int> FisherYatesShuffle(int length, int select, int repet)
{
if(select > length)
throw new ArgumentOutOfRangeException("在进行洗牌时,要求输出的项目比拥有的项目更多,这是不合理的。");
#if UNITY_EDITOR
if(repet > 100)
Debug.LogError("洗牌的重复次数太高(> 100),已限制数量");
#endif
repet = Math.Clamp(repet, 1, 100);
var random = new System.Random();
List<int> arr = Enumerable.Range(0, length).ToList();
List<int> res = new List<int>();
for(int j = 0; j < repet; j++)
{
for(int i = 0; i < length; i++)
{
int k = random.Next(arr.Count);
res.Add(arr[k]);
arr.RemoveAt(k);
}
arr = res.ToList();
res.Clear();
}
return arr;
}
/// <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]];
}
/// <summary>
/// 对列表中的成员按组进行划分,并读取特定组中特定索引下的成员
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="group"></param>
/// <param name="target"></param>
/// <returns></returns>
public static IEnumerable<T> GetListGroupMember<T>(this List<T> list, int group, int target)
{
if(list.Count % group != 0)
throw new Exception("在进行列组成员获取时,组不能刚好整除列表总数");
for(int i = 0; i < list.Count; i++)
if((i % group) == target)
yield return list[i];
}
#endregion
}
}