模仿 System.Random 提供的用户界面。可以从 System.Random 继承,也可以不。1、尽可能不重复。含义是,Next 方法在没有达到必定会重复的时候,就不能重复。
比如,生成21-24之间(含21和24)的伪随机数,则生成前4个数不能重复,当然,第5个数不可避免会重复的。
NextDouble 方法也要求返回尽可能不重复的数,也就是说,在耗尽大于或等于 0.0 而小于 1.0 的双精度浮点数之前,不得重复。
NextBytes 方法则可以要求在数组长度小于或等于 byte.MaxValue+1 (也就是256)时,返回的数组元素不得重复。若数组长度大于256,则要求前256个元素不得重复。2、随机性。我们知道,计算机不可能生成真正的随机序列,只能要求是伪随机序列。但我们可以要求这个序列不能有十分明显的规律,比如说,不能是等差数列、等比数列、斐波拿契数列,等等。3、高效。要求实现尽量高效,这可能会和随机性发生矛盾。比如,是否要在内部保存已生成的序列,以判定是否重复。以上抛砖引玉,还有很多方面都可以展开讨论。欢迎大家补充。
解决方案 »
- 画网格,线段均分的问题?谢谢
- 如何用eposn t58热敏打印机打印条码
- C#删除只读文件夹里面的文件
- 数据库与数据库的问题
- 怎样让C#中的控件在设计时可以响应事件
- 安装部署时怎么做成服务??
- 做一款软件,监控从交换机某个端口出去的所有数据包,该从哪入手
- 调用OWC10时用set_xlsRange.set_NumberFormat("@")以便让“002”显示为“002”而非"2"
- 请问如何将STRING类型转换为DATETIME类型?
- C#怎么实现用UDP进行多机通信?
- C#与C++如何共用内存?比如都访问一个结构体!注意,c#和C++在一个项目中
- 关于C#查看本机cup,内存的占用率的问题(除了windows的API外还有其它的方法吗?)
{
using System.Collections.Generic; class Random : System.Random
{
private List<int> int32List = new List<int>();
public override int Next(int minValue, int maxValue)
{
if (minValue == maxValue) return minValue;
int next = base.Next(minValue, maxValue);
if (int32List.Count >= maxValue - minValue) return next;
if (int32List.Contains(next)) return Next(minValue, maxValue);
int32List.Add(next);
return next;
}
}
}namespace Test
{
using System;
using Rand = Skyiv.Random;
class Program
{
static void Main()
{
Rand r = new Rand();
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
}
}
}
Console.WriteLine(r.Next(15,99)); // 1
Console.WriteLine(r.Next(10,80)); // 2
Console.WriteLine(r.Next(21,25)); // 3
Console.WriteLine(r.Next(21,23)); // 4
Console.WriteLine(r.Next(21,22)); // 5
如果是象上面那样调用,比如说,在第4次调用时,就应该检查前面的序列中是否已包含21和22,如果是,就不可避免重复,否则,就应该返回与前面不重复的整数。上面的实现就未必正确了。
如果每次返回 (new System.Random((上次的返回值+1)%区间长度)).Next(minValue, maxValue),是否可以保证不重复呢?随机性又如何?
{
class Random : System.Random
{
public Random(int Seed) : base(Seed){}
}
}还有其他的方法也需要重写,如public virtual int Next()
public virtual int Next(int maxValue)
public virtual double NextDouble()
public virtual void NextBytes(byte[] buffer)
生成的是字符串,如果生成数字的话,重复值会上升,耗时可能会更长
{
using System.Collections.Generic; class Random : System.Random
{
private List<double> doubleList = new List<double>(); public Random(int Seed) : base(Seed){} public override double NextDouble()
{
double next = base.NextDouble();
if (doubleList.Contains(next)) return NextDouble();
doubleList.Add(next);
return next;
}
}
}NextDouble 似可这样实现,请大家看看还有什么更高效的实现。另,这种实现在耗尽大于或等于 0.0 而小于 1.0 的双精度浮点数后,会陷入无限递归,导致用完stack而抛出异常。但,大于或等于 0.0 而小于 1.0 的双精度浮点数不知有多少个,有谁知道吗?我想应该是非常非常多的。
{
get { return doubleList; }
}问题是,怎么防止外部程序更改这个序列呢?
{
get { return doubleList.AsReadOnly(); }
}
而 double.Epsilon 字段的含义是:表示大于零的最小正 double。两个看来相等的浮点数字的比较结果可能为不相等,因为它们的最低有效位不同。例如,C# 表达式 (double)1/3 == (double)0.33333 的比较结果为不相等,因为左侧的除法运算具有最大精度,而右侧的常数仅精确到可见位。相反,它通过比较左右两边的差的绝对值是否小于 double.Epsilon,确定比较的双方是否近似相等。
*/接16楼的话题,我们是否可以猜测,大于或等于 0.0 而小于 1.0 的双精度浮点数的个数是不是大约为 1/double.Epsilon,也就是 2.0240225330731E+323。
{
using System;
using Rand = Skyiv.Random;
class Program
{
static void Main()
{
Rand r = new Rand();
int N = 10000;
int[] array = new int[N + 1];
for (int i = 0; i < array.Length - 1; i++)
{
array[i] = r.Next(0, array.Length * 2);
}
Console.WriteLine(array[0]);
Console.WriteLine(array[array.Length - 2]);
// 以上生成 10000 个不重复的伪随机数,这没问题。 // 在已经有长度为 10000 的序列后,下面要生成一个 50-79 之间的随机数,
// 就该检查是不是已经不可能不重复了,也就是前面 10000 个数是不是已经完整地覆盖了 50-79。
// 如果不是,就应该返回一个不重复的伪随机数,否则,只好随机地返回一个 50-79 之间的整数。
// 而我1楼的程序就不符合以上要求,极有可能在可以不重复的情况下返回一个重复的伪随机数。
array[array.Length - 1] = r.Next(50, 80);
Console.WriteLine(array[array.Length - 1]);
}
}
}
{
using System.Collections.Generic; class Random : System.Random
{
private List<int> int32List = new List<int>();
public override int Next(int minValue, int maxValue)
{
if (minValue == maxValue) return minValue;
int next = base.Next(minValue, maxValue);
if (int32List.Count >= maxValue - minValue) return next;
if (int32List.Contains(next)) return Next(minValue, maxValue);
int32List.Add(next);
return next;
}
}
}namespace Test
{
using System;
using Rand = Skyiv.Random;
class Program
{
static void Main()
{
Rand r = new Rand();
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
Console.WriteLine(r.Next(21,25));
}
}
}
1: 把你最终需要的结果(不重复的数)预先放在一个数组中, 因为rand函数产生的随机数会重复,我们不直接用,而是用来对应数组的下标
2: rand产生一个随机下标,我们就取出对应数组中的值(我们真正需要的随机数)
3: 然后用数组最后一个值来替换该下标数组中的值
4: 将产生随机下标的范围减少一
5: goto 2注: 3中所谓数组最后一个值是指产生随机下标范围内的最后一个.
如产生随机下标0-9, 第一次就用array[9]替换,第二次就用array[8]替换.#include<time.h>
#include<stdlib.h>
#include <stdio.h>#define NUM 10int main()
{
int cont[NUM];
int index, i; for (i=0; i<NUM; i++)
cont[i] = i; srand((int)time(0)); for (i=0; i<NUM; i++) {
index = (int)((float)(NUM-i) * rand() / (RAND_MAX+1.0));
printf("%d ", cont[index]);
cont[index] = cont[NUM-1-i];
}
putchar('\n'); return 0;
}
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; class Random : System.Random
{
private List<int> int32List = new List<int>(); public ReadOnlyCollection<int> Int32List
{
get { return int32List.AsReadOnly(); }
} public override int Next(int minValue, int maxValue)
{
return Next(minValue, maxValue, true);
}
private int Next(int minValue, int maxValue, bool check)
{
bool full = false;
if (check)
{
if (minValue > maxValue)
throw new ArgumentOutOfRangeException("minValue", "“minValue”不能大于 maxValue");
if (minValue == maxValue)
{
int32List.Add(minValue);
return minValue;
}
int n = 0;
for (int i = minValue; i < maxValue; i++)
{
if (int32List.Contains(i)) n++;
}
full = n == maxValue - minValue;
}
int next = base.Next(minValue, maxValue);
if (full) return next;
if (int32List.Contains(next)) return Next(minValue, maxValue, false);
int32List.Add(next);
return next;
}
}
}
{
return Next(0, int.MaxValue);
} public override int Next(int maxValue)
{
if (maxValue < 0)
throw new ArgumentOutOfRangeException("maxValue", "“maxValue”必须大于或等于零");
return Next(0, maxValue);
}
不要光路过,学习啊,我抛了那么多砖,也应该引一点玉来啊。象25楼godogoflive的想法也是一种不同的思路啊。
让玉来得更猛烈些吧!
{
using System.Collections.Generic; partial class Random : System.Random
{
private List<byte> byteList;
// 重写基类的方法
public override void NextBytes(byte[] buffer)
{
if (byteList != null) byteList.Clear();
byteList = new List<byte>(buffer.Length);
int i;
for (i = 0; i < buffer.Length && i < byte.MaxValue + 1; i++)
{
byte next = NextByte();
byteList.Add(next);
}
for (; i < buffer.Length; i++)
{
byte next = (byte)base.Next(byte.MaxValue + 1);
byteList.Add(next);
}
byteList.CopyTo(buffer);
}
private byte NextByte()
{
byte next = (byte)base.Next(byte.MaxValue + 1);
if (byteList.Contains(next)) return NextByte();
return next;
}
}
}
{
partial class Random : System.Random
{
public Random() : base() { }
public Random(int Seed) : base(Seed) { }
}
}
{
using System;
using Rand = Skyiv.Random; class Program
{
static void Main()
{
Rand r = new Rand();
byte[] bs = new byte[20];
r.NextBytes(bs);
foreach (byte b in bs)
{
Console.WriteLine(b);
}
Console.ReadLine();
}
}
}
{
using System.Collections.Generic;
using System.Collections.ObjectModel; partial class Random : System.Random
{
private List<double> doubleList = new List<double>(); // doubleList 的只读副本
public ReadOnlyCollection<double> DoubleList
{
get { return doubleList.AsReadOnly(); }
} // 重写基类的方法
public override double NextDouble()
{
double next = base.NextDouble();
if (doubleList.Contains(next)) return NextDouble();
doubleList.Add(next);
return next;
}
}
}
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; partial class Random : System.Random
{
private List<int> int32List = new List<int>(); public ReadOnlyCollection<int> Int32List
{
get { return int32List.AsReadOnly(); }
} public override int Next()
{
return Next(0, int.MaxValue);
} public override int Next(int maxValue)
{
if (maxValue < 0)
throw new ArgumentOutOfRangeException("maxValue", "“maxValue”必须大于或等于零");
return Next(0, maxValue);
} public override int Next(int minValue, int maxValue)
{
return Next(minValue, maxValue, true);
} private int Next(int minValue, int maxValue, bool check)
{
bool full = false;
if (check)
{
if (minValue > maxValue)
throw new ArgumentOutOfRangeException("minValue", "“minValue”不能大于 maxValue");
if (minValue == maxValue)
{
int32List.Add(minValue);
return minValue;
}
int n = 0;
for (int i = minValue; i < maxValue; i++)
{
if (int32List.Contains(i)) n++;
}
full = n == maxValue - minValue;
}
int next = base.Next(minValue, maxValue);
if (full) return next;
if (int32List.Contains(next)) return Next(minValue, maxValue, false);
int32List.Add(next);
return next;
}
}
}
第一维为随机数,第二维为等差序列
对该数组按照第一维排序,就得到第二维的随机顺序排列。
比如,生成21-24之间(含21和24)的伪随机数,则生成前4个数不能重复,当然,第5个数不可避免会重复的。
NextDouble 方法也要求返回尽可能不重复的数,也就是说,在耗尽大于或等于 0.0 而小于 1.0 的双精度浮点数之前,不得重复。
NextBytes 方法则可以要求在数组长度小于或等于 byte.MaxValue+1 (也就是256)时,返回的数组元素不得重复。若数组长度大于256,则要求前256个元素不得重复。 这个设计有问题,所谓(伪)随机数,是尽量随机得到一个数字,让人无法猜出下一个数会是什么。如果照lz说的,用生成21-24之间(含21和24)的伪随机数为例来说,那么我可以3次调用,得出第四个数是什么!这就失去了随机的意义了。如果lz想要高效,可靠的实现,建议使用Windows Crypt API,里面有专门的随机数发生器——CryptGenRandom,并且它是FIPS 140-1认可,这个在不少国家都认可的高效可靠算法。我看了ls的达人们的贴,基本上真正的随机数发生器都采用System.Random,而.NET的Random算法是线性的,lz可以读读下面的代码,这是从.NET shared source里摘抄的,有很详细的注释,也就是说,这样生成的数字依然是线性的,只不过线性数据中间某些被过滤而已。3楼的方法是可取的,RNGCryptoServiceProvider是.NET实现的为随机数发生器,和Crypt API基本上一样(实际内部还是有不少区别的,参考MSDN)。// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: Random
**
**
** Purpose: A random number generator.
**
**
===========================================================*/
namespace System {
using System;
using System.Runtime.CompilerServices;
using System.Globalization;
[System.Runtime.InteropServices.ComVisible(true)]
[Serializable()] public class Random {
//
// Private Constants
//
private const int MBIG = Int32.MaxValue;
private const int MSEED = 161803398;
private const int MZ = 0;
//
// Member Variables
//
private int inext, inextp;
private int[] SeedArray = new int[56];
//
// Public Constants
// //
// Native Declarations
//
//
// Constructors
// public Random()
: this(Environment.TickCount) {
} public Random(int Seed) {
int ii;
int mj, mk;
//Initialize our Seed array.
//This algorithm comes from Numerical Recipes in C (2nd Ed.)
mj = MSEED - Math.Abs(Seed);
SeedArray[55]=mj;
mk=1;
for (int i=1; i<55; i++) { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
ii = (21*i)%55;
SeedArray[ii]=mk;
mk = mj - mk;
if (mk<0) mk+=MBIG;
mj=SeedArray[ii];
}
for (int k=1; k<5; k++) {
for (int i=1; i<56; i++) {
SeedArray[i] -= SeedArray[1+(i+30)%55];
if (SeedArray[i]<0) SeedArray[i]+=MBIG;
}
}
inext=0;
inextp = 21;
Seed = 1;
} //
// Package Private Methods
//
/*====================================Sample====================================
**Action: Return a new random number [0..1) and reSeed the Seed array.
**Returns: A double [0..1)
**Arguments: None
**Exceptions: None
==============================================================================*/
protected virtual double Sample() {
//Including this division at the end gives us significantly improved
//random number distribution.
return (InternalSample()*(1.0/MBIG));
} private int InternalSample() {
int retVal;
int locINext = inext;
int locINextp = inextp; if (++locINext >=56) locINext=1;
if (++locINextp>= 56) locINextp = 1; retVal = SeedArray[locINext]-SeedArray[locINextp];
if (retVal<0) retVal+=MBIG;
SeedArray[locINext]=retVal; inext = locINext;
inextp = locINextp; return retVal;
} //
// Public Instance Methods
//
/*=====================================Next=====================================
**Returns: An int [0..Int32.MaxValue)
**Arguments: None
**Exceptions: None.
==============================================================================*/
public virtual int Next() {
return InternalSample();
} private double GetSampleForLargeRange() {
// The distribution of double value returned by Sample
// is not distributed well enough for a large range.
// If we use Sample for a range [Int32.MinValue..Int32.MaxValue)
// We will end up getting even numbers only.
int result = InternalSample();
// Note we can't use addition here. The distribution will be bad if we do that.
bool negative = (InternalSample()%2 == 0) ? true : false; // decide the sign based on second sample
if( negative) {
result = -result;
}
double d = result;
d += (Int32.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
d /= 2*(uint)Int32.MaxValue - 1 ;
return d;
}
/*=====================================Next=====================================
**Returns: An int [minvalue..maxvalue)
**Arguments: minValue -- the least legal value for the Random number.
** maxValue -- One greater than the greatest legal return value.
**Exceptions: None.
==============================================================================*/
public virtual int Next(int minValue, int maxValue) {
if (minValue>maxValue) {
throw new ArgumentOutOfRangeException("minValue",String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_MinMaxValue"), "minValue", "maxValue"));
}
long range = (long)maxValue-minValue;
if( range <= (long)Int32.MaxValue) {
return ((int)(Sample() * range) + minValue);
}
else {
return (int)((long)(GetSampleForLargeRange() * range) + minValue);
}
}
/*=====================================Next=====================================
**Returns: An int [0..maxValue)
**Arguments: maxValue -- One more than the greatest legal return value.
**Exceptions: None.
==============================================================================*/
public virtual int Next(int maxValue) {
if (maxValue<0) {
throw new ArgumentOutOfRangeException("maxValue", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "maxValue"));
}
return (int)(Sample()*maxValue);
}
/*=====================================Next=====================================
**Returns: A double [0..1)
**Arguments: None
**Exceptions: None
==============================================================================*/
public virtual double NextDouble() {
return Sample();
}
/*==================================NextBytes===================================
**Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled.
**Returns:Void
**Arugments: buffer -- the array to be filled.
**Exceptions: None
==============================================================================*/
public virtual void NextBytes(byte [] buffer){
if (buffer==null) throw new ArgumentNullException("buffer");
for (int i=0; i<buffer.Length; i++) {
buffer[i]=(byte)(InternalSample()%(Byte.MaxValue+1));
}
}
}
}
private void button2_Click(object sender, EventArgs e)
{
Dictionary<double, object> test = new Dictionary<double, object>();
int DoubleCount = 0;
for (int bi1 = 0; bi1 < 10000000; bi1++)
{
Guid temp = Guid.NewGuid();
double Return = BitConverter.ToDouble(temp.ToByteArray(), 0);
if (test.ContainsKey(Return))
{
DoubleCount++;
}
else
{
test.Add(Return,null);
}
}
this.Text = DoubleCount.ToString(); }LZ看看这段代码...1千万个随机数,没有一个重复..
Guid temp = Guid.NewGuid();
double Return = BitConverter.ToDouble(temp.ToByteArray(), 0);这是实际的随机数产生代码..
Dictionary<double, object> test = new Dictionary<double, object>();
int DoubleCount = 0;
for (int a = 0; a < 10000000; a++)
{
Guid temp = Guid.NewGuid();
ulong bi1 = BitConverter.ToUInt64(temp.ToByteArray(), 0);
bi1 = bi1 & 0x7FFFFFFFFFFFF;//00000000 00001111 11111111 11111111 11111111 11111111 11111111 1111111
bi1 = bi1 | 0x3FF8000000000000;
byte[] bb = BitConverter.GetBytes(bi1);
double Return = BitConverter.ToDouble(bb, 0);
if (test.ContainsKey(Return))
{
DoubleCount++;
}
else
{
test.Add(Return,null);
}
实用的纯小数代码...1去不掉了,不行就执行一个减1操作..
Dictionary<double, object> test = new Dictionary<double, object>();
int DoubleCount = 0;
for (int a = 0; a < 10000000; a++)
{
Guid temp = Guid.NewGuid();
ulong bi1 = BitConverter.ToUInt64(temp.ToByteArray(), 0);
bi1 = bi1 & 0x7FFFFFFFFFFFF;//00000000 00001111 11111111 11111111 11111111 11111111 11111111 1111111
bi1 = bi1 | 0x3FF8000000000000;
byte[] bb = BitConverter.GetBytes(bi1);
double Return = BitConverter.ToDouble(bb, 0);
if (test.ContainsKey(Return))
{
DoubleCount++;
}
else
{
test.Add(Return,null);
}
那这个算法没有满足楼主要求吗?
{
static List<int> GetRandom(int maxCount)
{
Random rnd = new Random();
List<int> lsOld = new List<int>();
//为一个泛型集合添加1到maxCount的顺序数字
for (int i = 0; i < maxCount; i++)
{
lsOld.Add(i);
} int temp;
int tempRnd;
for (int i = maxCount; i > 0; i--)
{
tempRnd = rnd.Next(0, i - 1);
temp = lsOld[i - 1];
lsOld[i - 1] = lsOld[tempRnd];
lsOld[tempRnd] = temp;
}
return lsOld;
}
static void Main()
{
//只添加100个不重复的随即数
List<int> lsAll; DateTime startTime = DateTime.Now; lsAll = GetRandom(1000000);
//foreach (int i in lsAll)
//{
// Console.WriteLine(i);
//} TimeSpan timeSpan = DateTime.Now - startTime; Console.WriteLine("共消耗时间:" + timeSpan.TotalMilliseconds + "毫秒");
Console.ReadKey(); }
}
那么
1-N之间随机整数概率将在N/2次子后变大例如N=5
第一次 3 概率1/5
第二次 1 概率1/4
第三次 4 概率1/3
第四次 2 概率1/2
第五次 5 概率1/1 必然没有意义啊
在大数量级下一般用循环链表来解决这个问题。即将值域中所有值作为一个循环链表,从第一个节点开始,取一个随机数便向后推进随机个节点,输出这个节点并将其从循环链表中删除。但是如果要在链表大小范围内抽取随机数,或许向后检索一个很大的个数的时候性能不够好,即使采用双向检索(将超过链表一半大小的检索转变为向后检索)也不能获得很好的性能,那么可以参考十字链表的做法,做一个循环十字链表。如下图结构: A1 -> A2 -> A3 -> A4 ..... A10 ->接A11
↓ ↓ ↓ ↓ ↓
->A11 A12 A13 A14.... A20
....
这里是按照十进制做的二维,如果我们要从A1向后14项,那么只需要按照向下的顺序向后推进1项,向前的顺序推进4项即可。
当然,因为中间有大量io操作,效率降低在所难免。而取随机数,上文的朋友提到是一个正态分布或者泊松分布的问题,我也这么认为,那么,当你的取值区间很小时,你的重复的概率就高。 那么,我们可以通过增大取值区间来降低重复的概率。
说得不好,请大家不要见笑。
1、MaxValue和MinValue只能通过new输入,创建好之后不能改变(否则就麻烦了)
2、Count=MaxValue-MinValue
3、随机产生n个数字(m1,m2,m3...mn),保证这些数字与Count互质
4、用每一个数字做等差数列,例如:
f1(x)=m1*x%Count;
f2(x)=f1(m2*x%Count);
...
fn(x)=fn-1(mn*x%Count);
5、每一次调用Next方法就是x++,然后对MinValue+fn(x)求解
6、当x=Count时,重新生成随机数m1,m2,m3...mn,x重置为0,达到重新排列的目的
附:可以通过增加m的数量,来减少规律
我是用链表保存了所有的4位数的值(for循环加的),然后用随机数当作链表的位置,
取出来一个就把这个数在链表中删掉,每次random的最大值-1