001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.util; 019 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collections; 023import java.util.Random; 024import org.apache.yetus.audience.InterfaceAudience; 025 026/** 027 * A class that generates random numbers that follow some distribution. 028 * <p> 029 * Copied from <a href="https://issues.apache.org/jira/browse/HADOOP-3315">hadoop-3315 tfile</a>. 030 * Remove after tfile is committed and use the tfile version of this class instead. 031 * </p> 032 */ 033@InterfaceAudience.Private 034public class RandomDistribution { 035 /** 036 * Interface for discrete (integer) random distributions. 037 */ 038 public interface DiscreteRNG { 039 /** 040 * Get the next random number 041 * @return the next random number. 042 */ 043 int nextInt(); 044 } 045 046 /** 047 * P(i)=1/(max-min) 048 */ 049 public static final class Flat implements DiscreteRNG { 050 private final Random random; 051 private final int min; 052 private final int max; 053 054 /** 055 * Generate random integers from min (inclusive) to max (exclusive) following even distribution. 056 * The basic random number generator. Minimum integer maximum integer (exclusive). 057 */ 058 public Flat(Random random, int min, int max) { 059 if (min >= max) { 060 throw new IllegalArgumentException("Invalid range"); 061 } 062 this.random = random; 063 this.min = min; 064 this.max = max; 065 } 066 067 /** 068 * @see DiscreteRNG#nextInt() 069 */ 070 @Override 071 public int nextInt() { 072 return random.nextInt(max - min) + min; 073 } 074 } 075 076 /** 077 * Zipf distribution. The ratio of the probabilities of integer i and j is defined as follows: 078 * P(i)/P(j)=((j-min+1)/(i-min+1))^sigma. 079 */ 080 public static final class Zipf implements DiscreteRNG { 081 private static final double DEFAULT_EPSILON = 0.001; 082 private final Random random; 083 private final ArrayList<Integer> k; 084 private final ArrayList<Double> v; 085 086 /** 087 * Constructor The random number generator. minimum integer (inclusvie) maximum integer 088 * (exclusive) parameter sigma. (sigma > 1.0) 089 */ 090 public Zipf(Random r, int min, int max, double sigma) { 091 this(r, min, max, sigma, DEFAULT_EPSILON); 092 } 093 094 /** 095 * Constructor. The random number generator. minimum integer (inclusvie) maximum integer 096 * (exclusive) parameter sigma. (sigma > 1.0) Allowable error percentage (0 < epsilon < 1.0). 097 */ 098 public Zipf(Random r, int min, int max, double sigma, double epsilon) { 099 if ((max <= min) || (sigma <= 1) || (epsilon <= 0) || (epsilon >= 0.5)) { 100 throw new IllegalArgumentException("Invalid arguments"); 101 } 102 random = r; 103 k = new ArrayList<>(); 104 v = new ArrayList<>(); 105 106 double sum = 0; 107 int last = -1; 108 for (int i = min; i < max; ++i) { 109 sum += Math.exp(-sigma * Math.log(i - min + 1)); 110 if ((last == -1) || i * (1 - epsilon) > last) { 111 k.add(i); 112 v.add(sum); 113 last = i; 114 } 115 } 116 117 if (last != max - 1) { 118 k.add(max - 1); 119 v.add(sum); 120 } 121 122 v.set(v.size() - 1, 1.0); 123 124 for (int i = v.size() - 2; i >= 0; --i) { 125 v.set(i, v.get(i) / sum); 126 } 127 } 128 129 /** 130 * @see DiscreteRNG#nextInt() 131 */ 132 @Override 133 public int nextInt() { 134 double d = random.nextDouble(); 135 int idx = Collections.binarySearch(v, d); 136 137 if (idx > 0) { 138 ++idx; 139 } else { 140 idx = -(idx + 1); 141 } 142 143 if (idx >= v.size()) { 144 idx = v.size() - 1; 145 } 146 147 if (idx == 0) { 148 return k.get(0); 149 } 150 151 int ceiling = k.get(idx); 152 int lower = k.get(idx - 1); 153 154 return ceiling - random.nextInt(ceiling - lower); 155 } 156 } 157 158 /** 159 * Binomial distribution. P(k)=select(n, k)*p^k*(1-p)^(n-k) (k = 0, 1, ..., n) 160 * P(k)=select(max-min-1, k-min)*p^(k-min)*(1-p)^(k-min)*(1-p)^(max-k-1) 161 */ 162 public static final class Binomial implements DiscreteRNG { 163 private final Random random; 164 private final int min; 165 private final int n; 166 private final double[] v; 167 168 private static double select(int n, int k) { 169 double ret = 1.0; 170 for (int i = k + 1; i <= n; ++i) { 171 ret *= (double) i / (i - k); 172 } 173 return ret; 174 } 175 176 private static double power(double p, int k) { 177 return Math.exp(k * Math.log(p)); 178 } 179 180 /** 181 * Generate random integers from min (inclusive) to max (exclusive) following Binomial 182 * distribution. The basic random number generator. Minimum integer maximum integer (exclusive). 183 * parameter. 184 */ 185 public Binomial(Random random, int min, int max, double p) { 186 if (min >= max) { 187 throw new IllegalArgumentException("Invalid range"); 188 } 189 this.random = random; 190 this.min = min; 191 this.n = max - min - 1; 192 if (n > 0) { 193 v = new double[n + 1]; 194 double sum = 0.0; 195 for (int i = 0; i <= n; ++i) { 196 sum += select(n, i) * power(p, i) * power(1 - p, n - i); 197 v[i] = sum; 198 } 199 for (int i = 0; i <= n; ++i) { 200 v[i] /= sum; 201 } 202 } else { 203 v = null; 204 } 205 } 206 207 /** 208 * @see DiscreteRNG#nextInt() 209 */ 210 @Override 211 public int nextInt() { 212 if (v == null) { 213 return min; 214 } 215 double d = random.nextDouble(); 216 int idx = Arrays.binarySearch(v, d); 217 if (idx > 0) { 218 ++idx; 219 } else { 220 idx = -(idx + 1); 221 } 222 223 if (idx >= v.length) { 224 idx = v.length - 1; 225 } 226 return idx + min; 227 } 228 } 229}