![JAR search and dependency download from the Maven repository](/logo.png)
net.hasor.cobble.RandomUtils Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.hasor.cobble;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Random;
import static java.math.BigDecimal.ROUND_DOWN;
/**
* Utility library that supplements the standard {@link Random} class.
*
* Caveat: Instances of {@link Random} are not cryptographically secure.
*
* Provides randomness between any two numbers, For example: arbitrarily large numbers, negative numbers
*
* that may be a better choice for applications with more stringent requirements
*
* @author 赵永春 ([email protected])
*/
public class RandomUtils {
/**
* Random object used by random method. This has to be not local to the
* random method so as to not return the same value in the same millisecond.
*/
private static final Random RANDOM = new Random(System.currentTimeMillis());
/**
*
* {@code RandomUtils} instances should NOT be constructed in standard
* programming. Instead, the class should be used as
* {@code RandomUtils.nextBytes(5);}.
*
*
*
* This constructor is public to permit tools that require a JavaBean instance to operate.
*
*/
public RandomUtils() {
super();
}
private static void isTrue(final boolean expression, final String message, final Object... values) {
if (!expression) {
throw new IllegalArgumentException(String.format(message, values));
}
}
/**
* Returns a random boolean value
*
* @return the random boolean
*/
public static boolean nextBoolean() {
return RANDOM.nextBoolean();
}
/**
* Creates an array of random bytes.
*
* @param count the size of the returned array
* @return the random byte array
* @throws IllegalArgumentException if {@code count} is negative
*/
public static byte[] nextBytes(final int count) {
isTrue(count >= 0, "Count cannot be negative.");
final byte[] result = new byte[count];
RANDOM.nextBytes(result);
return result;
}
/**
* fill an array of random bytes.
*
* @param b fill array
* @throws IllegalArgumentException if {@code count} is negative
*/
public static void nextBytes(byte[] b) {
RANDOM.nextBytes(b);
}
/**
* Returns a random integer within the specified range.
*
* @param startInclusive the smallest value that can be returned, must be non-negative
* @param endInclusive the upper bound (included)
* @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if {@code startInclusive} is negative
* @return the random integer
*/
public static int nextInt(int startInclusive, int endInclusive) {
return nextBigInteger(startInclusive, endInclusive).intValue();
}
/**
* Returns a random int within 0 - Integer.MAX_VALUE
*
* @return the random integer
* @see #nextInt(int, int)
*/
public static int nextInt() {
return nextBigInteger(0, Integer.MAX_VALUE).intValue();
}
/**
*
* Returns a random long within the specified range.
*
*
* @param startInclusive the smallest value that can be returned, must be non-negative
* @param endInclusive the upper bound (included)
* @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if {@code startInclusive} is negative
* @return the random long
*/
public static long nextLong(long startInclusive, long endInclusive) {
return nextBigInteger(startInclusive, endInclusive).longValue();
}
/**
* Returns a random long within 0 - Long.MAX_VALUE
*
* @return the random long
* @see #nextLong(long, long)
*/
public static long nextLong() {
return nextBigInteger(0, Long.MAX_VALUE).longValue();
}
/**
*
* Returns a random BigInteger within the specified range.
*
*
* @param startInclusive the smallest value that can be returned, support negative
* @param endInclusive the upper bound (included)
* @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if {@code startInclusive} is negative
* @return the random long
*/
public static BigInteger nextBigInteger(Number startInclusive, Number endInclusive) {
BigInteger minBig = (startInclusive instanceof BigInteger) ? (BigInteger) startInclusive : (startInclusive instanceof BigDecimal) ? ((BigDecimal) startInclusive).toBigInteger() : (BigInteger.valueOf(startInclusive.longValue()));
BigInteger maxBig = (endInclusive instanceof BigInteger) ? (BigInteger) endInclusive : (endInclusive instanceof BigDecimal) ? ((BigDecimal) endInclusive).toBigInteger() : (BigInteger.valueOf(endInclusive.longValue()));
int signum = minBig.signum() == maxBig.signum() ? minBig.signum() : 1;
BigInteger offset;
BigInteger bigRange;
if (signum == -1 || minBig.equals(BigInteger.ZERO)) {
offset = minBig.abs();
bigRange = minBig.abs().add(maxBig.abs());
} else {
offset = minBig.negate();
bigRange = maxBig.subtract(minBig);
}
StringBuilder result = new StringBuilder();
boolean inFree = false;
char[] bitChars = bigRange.toString(2).toCharArray();
for (char rangeChar : bitChars) {
if (inFree) {
result.append(nextBoolean() ? 1 : 0);
} else {
boolean oriBit = rangeChar == '1';
boolean newBit = nextBoolean();
inFree = oriBit != newBit && oriBit;
if (inFree) {
result.append(0);
} else {
result.append(oriBit ? 1 : 0);
}
}
}
BigInteger after = new BigInteger(result.toString(), 2);
if (signum == -1) {
return after.negate().subtract(offset);
} else {
return after.subtract(offset);
}
}
/**
*
* Returns a random double within the specified range.
*
*
* @param startInclusive the smallest value that can be returned, support negative
* @param endInclusive the upper bound (included)
* @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if {@code startInclusive} is negative
* @return the random double
*/
public static double nextDouble(final double startInclusive, final double endInclusive) {
isTrue(endInclusive >= startInclusive, "Start value must be smaller or equal to end value.");
isTrue(startInclusive >= 0, "Both range values support negative.");
if (startInclusive == endInclusive) {
return startInclusive;
}
return startInclusive + ((endInclusive - startInclusive) * RANDOM.nextDouble());
}
/**
* Returns a random double within 0 - Double.MAX_VALUE
*
* @return the random double
* @see #nextDouble(double, double)
* @since 3.5
*/
public static double nextDouble() {
return nextDouble(0, Double.MAX_VALUE);
}
/**
*
* Returns a random float within the specified range.
*
*
* @param startInclusive the smallest value that can be returned, support negative
* @param endInclusive the upper bound (included)
* @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if {@code startInclusive} is negative
* @return the random float
*/
public static float nextFloat(final float startInclusive, final float endInclusive) {
isTrue(endInclusive >= startInclusive, "Start value must be smaller or equal to end value.");
isTrue(startInclusive >= 0, "Both range values support negative.");
if (startInclusive == endInclusive) {
return startInclusive;
}
return startInclusive + ((endInclusive - startInclusive) * RANDOM.nextFloat());
}
/**
* Returns a random float within 0 - Float.MAX_VALUE
*
* @return the random float
* @see #nextFloat()
*/
public static float nextFloat() {
return nextFloat(0, Float.MAX_VALUE);
}
/**
*
* Returns a random BigDecimal within the specified range.
*
*
* @param startInclusive the smallest value that can be returned, support negative
* @param endInclusive the upper bound (included)
* @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if {@code startInclusive} is negative
* @return the random BigDecimal
*/
public static BigDecimal nextDecimal(Number startInclusive, Number endInclusive, Integer scale) {
BigDecimal minBig = (startInclusive instanceof BigDecimal) ? (BigDecimal) startInclusive : (startInclusive instanceof BigInteger) ? new BigDecimal((BigInteger) startInclusive) : (BigDecimal.valueOf(startInclusive.doubleValue()));
BigDecimal maxBig = (endInclusive instanceof BigDecimal) ? (BigDecimal) endInclusive : (endInclusive instanceof BigInteger) ? new BigDecimal((BigInteger) endInclusive) : (BigDecimal.valueOf(endInclusive.doubleValue()));
if (scale == null) {
BigDecimal[] minParts = minBig.divideAndRemainder(BigDecimal.ONE);
BigDecimal[] maxParts = maxBig.divideAndRemainder(BigDecimal.ONE);
scale = Math.max(minParts[1].scale(), maxParts[1].scale());
}
BigDecimal result;
if (scale == 0) {
result = new BigDecimal(nextBigInteger(minBig, maxBig));
} else {
BigDecimal scaleMul = new BigDecimal("1" + StringUtils.repeat("0", scale));
BigDecimal scaleMin = minBig.multiply(scaleMul);
BigDecimal scaleMax = maxBig.multiply(scaleMul);
result = new BigDecimal(nextBigInteger(scaleMin, scaleMax)).divide(scaleMul);
}
return result.setScale(scale, ROUND_DOWN);
}
/**
*
* Returns a random BigDecimal within the specified range.
*
*
* @param precision precision of random number
* @param scale scale of random number
* @return the random BigDecimal
*/
public static BigDecimal nextDecimal(Integer precision, Integer scale) {
if (precision == null && scale == null) {
return BigDecimal.valueOf(nextDouble());
}
if (precision == null) {
precision = scale;
}
if (scale == null) {
scale = 0;
}
BigDecimal randomDecimal = new BigDecimal(nextBigInteger(0, new BigInteger(StringUtils.repeat("9", precision))));
BigDecimal divNum = new BigDecimal("1" + StringUtils.repeat("0", scale));
randomDecimal = randomDecimal.divide(divNum, scale, ROUND_DOWN).stripTrailingZeros();
return nextBoolean() ? randomDecimal : randomDecimal.negate();
}
}