Случайное число на большом расстоянии, так ли?
Может ли кто-нибудь проверить этот метод. Мне нужен длинный номер в пределах двух длин. Я использую функцию .NET Random.Next(min, max), возвращающую int. Правильно ли я рассуждаю, если я просто разделил длинный на 2, сгенерировал случайное число и, наконец, снова умножил его на 2? Или я слишком восторжен...
Я понимаю, что мое случайное разрешение уменьшится, но есть ли другие ошибки, которые не приведут к такому случайному числу.
long min = st.MinimumTime.Ticks; //long is Signed 64-bit integer
long max = st.MaximumTime.Ticks;
int minInt = (int) (min / 2); //int is Signed 64-bit integer
int maxInt = (int) (max / 2); //int is Signed 64-bit integer
Random random = new Random();
int randomInt = random.Next(minInt, maxInt);
long randomLong = (randomInt * 2);
Ответы
Ответ 1
Почему бы вам просто не создать два случайных значения Int32
и сделать из них Int64
?
long LongRandom(long min, long max, Random rand) {
long result = rand.Next((Int32)(min >> 32), (Int32)(max >> 32));
result = (result << 32);
result = result | (long)rand.Next((Int32)min, (Int32)max);
return result;
}
Извините, я забыл добавлять границы в первый раз. Добавлены параметры min
и max
. Вы можете проверить это следующим образом:
long r = LongRandom(100000000000000000, 100000000000000050, new Random());
Значения r
будут находиться в нужном диапазоне.
РЕДАКТИРОВАТЬ: описанная реализация ошибочна. Вероятно, это стоит того, чтобы генерировать 4 16-битных целых числа, а не 2 32-разрядных, чтобы избежать проблем с подписью без знака. Но на данный момент решение теряет свою элегантность, поэтому я думаю, что лучше всего придерживаться версии Random.NextBytes
:
long LongRandom(long min, long max, Random rand) {
byte[] buf = new byte[8];
rand.NextBytes(buf);
long longRand = BitConverter.ToInt64(buf, 0);
return (Math.Abs(longRand % (max - min)) + min);
}
Это выглядит довольно хорошо с точки зрения распределения стоимости (судя по очень простым тестам, которые я запускал).
Ответ 2
Это создает случайный Int64 с использованием случайных байтов, избегая смещения по модулю путем повторной попытки, если число находится за пределами безопасного диапазона.
static class RandomExtensions
{
public static long RandomLong(this Random rnd)
{
byte[] buffer = new byte[8];
rnd.NextBytes (buffer);
return BitConverter.ToInt64(buffer, 0);
}
public static long RandomLong(this Random rnd, long min, long max)
{
EnsureMinLEQMax(ref min, ref max);
long numbersInRange = unchecked(max - min + 1);
if (numbersInRange < 0)
throw new ArgumentException("Size of range between min and max must be less than or equal to Int64.MaxValue");
long randomOffset = RandomLong(rnd);
if (IsModuloBiased(randomOffset, numbersInRange))
return RandomLong(rnd, min, max); // Try again
else
return min + PositiveModuloOrZero(randomOffset, numbersInRange);
}
static bool IsModuloBiased(long randomOffset, long numbersInRange)
{
long greatestCompleteRange = numbersInRange * (long.MaxValue / numbersInRange);
return randomOffset > greatestCompleteRange;
}
static long PositiveModuloOrZero(long dividend, long divisor)
{
long mod;
Math.DivRem(dividend, divisor, out mod);
if(mod < 0)
mod += divisor;
return mod;
}
static void EnsureMinLEQMax(ref long min, ref long max)
{
if(min <= max)
return;
long temp = min;
min = max;
max = temp;
}
}
Ответ 3
В некоторых других ответах есть два вопроса: наличие модульного смещения и невозможность корректно обрабатывать значения max = long.MaxValue
. (ответ Мартина не имеет проблем, но его код необоснованно медленный с большими диапазонами.)
Следующий код исправит все эти проблемы:
//Working with ulong so that modulo works correctly with values > long.MaxValue
ulong uRange = (ulong)(max - min);
//Prevent a modolo bias; see https://stackoverflow.com/a/10984975/238419
//for more information.
//In the worst case, the expected number of calls is 2 (though usually it's
//much closer to 1) so this loop doesn't really hurt performance at all.
ulong ulongRand;
do
{
byte[] buf = new byte[8];
random.NextBytes(buf);
ulongRand = (ulong)BitConverter.ToInt64(buf, 0);
} while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange);
return (long)(ulongRand % uRange) + min;
Следующий полностью документированный класс можно отбросить в вашу кодовую базу, чтобы реализовать это решение легко и без мозгов. Как и весь код в Stackoverflow, он лицензируется в соответствии с CC-атрибуцией, поэтому вы можете свободно использовать его для использования в основном независимо от того, что вы хотите.
using System;
namespace MyNamespace
{
public static class RandomExtensionMethods
{
/// <summary>
/// Returns a random long from min (inclusive) to max (exclusive)
/// </summary>
/// <param name="random">The given random instance</param>
/// <param name="min">The inclusive minimum bound</param>
/// <param name="max">The exclusive maximum bound. Must be greater than min</param>
public static long NextLong(this Random random, long min, long max)
{
if (max <= min)
throw new ArgumentOutOfRangeException("max", "max must be > min!");
//Working with ulong so that modulo works correctly with values > long.MaxValue
ulong uRange = (ulong)(max - min);
//Prevent a modolo bias; see https://stackoverflow.com/a/10984975/238419
//for more information.
//In the worst case, the expected number of calls is 2 (though usually it's
//much closer to 1) so this loop doesn't really hurt performance at all.
ulong ulongRand;
do
{
byte[] buf = new byte[8];
random.NextBytes(buf);
ulongRand = (ulong)BitConverter.ToInt64(buf, 0);
} while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange);
return (long)(ulongRand % uRange) + min;
}
/// <summary>
/// Returns a random long from 0 (inclusive) to max (exclusive)
/// </summary>
/// <param name="random">The given random instance</param>
/// <param name="max">The exclusive maximum bound. Must be greater than 0</param>
public static long NextLong(this Random random, long max)
{
return random.NextLong(0, max);
}
/// <summary>
/// Returns a random long over all possible values of long (except long.MaxValue, similar to
/// random.Next())
/// </summary>
/// <param name="random">The given random instance</param>
public static long NextLong(this Random random)
{
return random.NextLong(long.MinValue, long.MaxValue);
}
}
}
Использование:
Random random = new Random();
long foobar = random.NextLong(0, 1234567890L);
Ответ 4
Вот решение, которое использует другие ответы, используя Random.NextBytes
, но также уделяет пристальное внимание граничным случаям. Я структурировал его как набор методов расширения. Кроме того, я учитывал смещение по модулю, путем выборки другого случайного числа, которое выпадает из диапазона.
Одна из моих проблем (по крайней мере, для ситуации, в которой я пытался ее использовать) заключается в том, что максимум обычно является исключительным, поэтому, если вы хотите катить кубик, вы делаете что-то вроде Random.Next(0,7)
. Однако это означает, что вы никогда не сможете получить эту перегрузку, чтобы вернуть .MaxValue
для типа данных (int
, long
, ulong
, what-have-you). Поэтому я добавил флаг inclusiveUpperBound
для переключения этого поведения.
public static class Extensions
{
//returns a uniformly random ulong between ulong.Min inclusive and ulong.Max inclusive
public static ulong NextULong(this Random rng)
{
byte[] buf = new byte[8];
rng.NextBytes(buf);
return BitConverter.ToUInt64(buf, 0);
}
//returns a uniformly random ulong between ulong.Min and Max without modulo bias
public static ulong NextULong(this Random rng, ulong max, bool inclusiveUpperBound = false)
{
return rng.NextULong(ulong.MinValue, max, inclusiveUpperBound);
}
//returns a uniformly random ulong between Min and Max without modulo bias
public static ulong NextULong(this Random rng, ulong min, ulong max, bool inclusiveUpperBound = false)
{
ulong range = max - min;
if (inclusiveUpperBound)
{
if (range == ulong.MaxValue)
{
return rng.NextULong();
}
range++;
}
if (range <= 0)
{
throw new ArgumentOutOfRangeException("Max must be greater than min when inclusiveUpperBound is false, and greater than or equal to when true", "max");
}
ulong limit = ulong.MaxValue - ulong.MaxValue % range;
ulong r;
do
{
r = rng.NextULong();
} while(r > limit);
return r % range + min;
}
//returns a uniformly random long between long.Min inclusive and long.Max inclusive
public static long NextLong(this Random rng)
{
byte[] buf = new byte[8];
rng.NextBytes(buf);
return BitConverter.ToInt64(buf, 0);
}
//returns a uniformly random long between long.Min and Max without modulo bias
public static long NextLong(this Random rng, long max, bool inclusiveUpperBound = false)
{
return rng.NextLong(long.MinValue, max, inclusiveUpperBound);
}
//returns a uniformly random long between Min and Max without modulo bias
public static long NextLong(this Random rng, long min, long max, bool inclusiveUpperBound = false)
{
ulong range = (ulong)(max - min);
if (inclusiveUpperBound)
{
if (range == ulong.MaxValue)
{
return rng.NextLong();
}
range++;
}
if (range <= 0)
{
throw new ArgumentOutOfRangeException("Max must be greater than min when inclusiveUpperBound is false, and greater than or equal to when true", "max");
}
ulong limit = ulong.MaxValue - ulong.MaxValue % range;
ulong r;
do
{
r = rng.NextULong();
} while(r > limit);
return (long)(r % range + (ulong)min);
}
}
Ответ 5
Ваш randomLong всегда будет четным, и вы исключите еще больше значений, потому что вы очень далеко от максимума для long
, максимум для long - 2 ^ 32 * max для int. Вы должны использовать Random.NextBytes
.
Ответ 6
Начните с минимума, добавьте случайный процент от разницы между min и max. Проблема в том, что NextDouble возвращает число x такое, что 0 <= x < 1, так что шанс, что вы никогда не нажмете максимальное количество.
long randomLong = min + (long)(random.NextDouble() * (max - min));
Ответ 7
Вы можете попробовать CryptoRandom
Inferno library:
public class CryptoRandom : Random
// implements all Random methods, as well as:
public byte[] NextBytes(int count)
public long NextLong()
public long NextLong(long maxValue)
public long NextLong(long minValue, long maxValue)
Ответ 8
Вам лучше не принимать разницу между минимальным и максимальным (если он подходит в int), получая случайное значение от 0 до этого и добавляя его к минимуму.
Ответ 9
Есть ли что-то неправильное в использовании этого простого подхода?
long min = 10000000000001;
long max = 99999999999999;
Random random = new Random();
long randomNumber = min + random.Next() % (max - min);
д
Ответ 10
Мое работающее решение. Протестировано более 1000 раз:
public static long RandomLong(long min, long max)
{
return min + (long)RandomULong(0, (ulong)Math.Abs(max - min));
}
public static ulong RandomULong(ulong min, ulong max)
{
var hight = Rand.Next((int)(min >> 32), (int)(max >> 32));
var minLow = Math.Min((int)min, (int)max);
var maxLow = Math.Max((int)min, (int)max);
var low = (uint)Rand.Next(minLow, maxLow);
ulong result = (ulong)hight;
result <<= 32;
result |= (ulong)low;
return result;
}
Ответ 11
private long randomLong()
{
Random random = new Random();
byte[] bytes = new byte[8];
_random.NextBytes(bytes);
return BitConverter.ToInt64(bytes, 0);
}
Ответ 12
Что не так с созданием double
для использования в качестве фактора, который будет использоваться для вычисления фактического значения long
, начиная с максимального значения a long
, может быть?!
long result = (long)Math.Round( random.NextDouble() * maxLongValue );
-
NextDouble
генерирует случайное число между [0.0, 0.99999999999999978]
(msdn doc)
-
Вы умножаете это случайное число на maxLongValue
.
-
Вы получите Math.Round
, чтобы получить возможность получить maxLongValue
в любом случае (например: имитировать, что вы получили 1.0 из NextDouble).
- Вы вернетесь к
long
.
Ответ 13
Как насчет генерации байтов и преобразования в int64?
/* generate a byte array, then convert to unint64 */
var r = new Random(); // DONT do this for each call - use a static Random somewhere
var barray = new byte[64/8];
r.NextBytes(barray);
var rint64 = BitConverter.ToUInt64(barray, 0);
Видит работать для меня (: