Як генерувати випадкові цілі числа в певному діапазоні на Java?


3500

Як генерувати випадкове intзначення у визначеному діапазоні?

Я спробував наступне, але це не працює:

Спроба 1:

randomNum = minimum + (int)(Math.random() * maximum);
// Bug: `randomNum` can be bigger than `maximum`.

Спроба 2:

Random rn = new Random();
int n = maximum - minimum + 1;
int i = rn.nextInt() % n;
randomNum =  minimum + i;
// Bug: `randomNum` can be smaller than `minimum`.

Коли вам потрібно багато випадкових чисел, я не рекомендую клас Random в API. У неї просто занадто малий період. Спробуйте замість цього твістер Mersenne . Є реалізація Java .
raupach

1
Перш ніж опублікувати нову відповідь, подумайте, що на це питання вже є 65+ відповідей. Будь ласка, переконайтеся, що ваша відповідь вносить інформацію, яка не входить до наявних відповідей.
janniks

Відповіді:


3797

У Java 1.7 або новіших версіях стандартний спосіб зробити це наступним чином:

import java.util.concurrent.ThreadLocalRandom;

// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1);

Дивіться відповідний JavaDoc . Цей підхід має перевагу в тому, що не потрібно явно ініціалізувати примірник java.util.Random , який може бути джерелом плутанини та помилок при неправильному використанні.

Однак, навпаки, немає способу явного встановлення насіння, тому може бути важко відтворити результати в ситуаціях, коли це корисно, наприклад, тестування чи збереження ігрових станів чи подібних. У таких ситуаціях може використовуватися техніка попередньої Java 1.7, показана нижче.

Перед Java 1.7 стандартний спосіб зробити це наступним чином:

import java.util.Random;

/**
 * Returns a pseudo-random number between min and max, inclusive.
 * The difference between min and max can be at most
 * <code>Integer.MAX_VALUE - 1</code>.
 *
 * @param min Minimum value
 * @param max Maximum value.  Must be greater than min.
 * @return Integer between min and max, inclusive.
 * @see java.util.Random#nextInt(int)
 */
public static int randInt(int min, int max) {

    // NOTE: This will (intentionally) not run as written so that folks
    // copy-pasting have to think about how to initialize their
    // Random instance.  Initialization of the Random instance is outside
    // the main scope of the question, but some decent options are to have
    // a field that is initialized once and then re-used as needed or to
    // use ThreadLocalRandom (if using at least Java 1.7).
    // 
    // In particular, do NOT do 'Random rand = new Random()' here or you
    // will get not very good / not very random results.
    Random rand;

    // nextInt is normally exclusive of the top value,
    // so add 1 to make it inclusive
    int randomNum = rand.nextInt((max - min) + 1) + min;

    return randomNum;
}

Дивіться відповідний JavaDoc . На практиці клас java.util.Random часто є кращим порівняно з java.lang.Math.random () .

Зокрема, не потрібно винаходити колесо генерації випадкових цілих чисел, коли в стандартній бібліотеці є прямий API для виконання завдання.


43
Для дзвінків, де maxзначення є Integer.MAX_VALUEможливим переповненням, в результаті чого стає a java.lang.IllegalArgumentException. Ви можете спробувати: randInt(0, Integer.MAX_VALUE). Крім того, якщо nextInt((max-min) + 1)поверне найвище значення (я вважаю досить рідкісним, я вважаю), воно не буде переповнене знову (припустимо, що min та max є досить високими значеннями)? Як боротися з подібними ситуаціями?
Даніель

3
@MoisheLipsker Треба, що nextLong не приймає обмеження як nextInteger
mmm

13
@leventov ThreadLocalRandomбув доданий до Java 2 1/2 року після першого запитання. Я завжди був думкою, що управління випадковою інстанцією знаходиться поза межами питання.
Грег Кейс

6
В Android Random rand = новий Random ();
Webserveis

5
@Webserveis Це розглянуто в коментарях у прикладі. Коротка версія - ви не повинні = новий Random () для кожного виклику функції, інакше ваші результати не будуть достатньо випадковими для багатьох випадків.
Грег Кейс

1421

Зауважте, що цей підхід є більш упередженим та менш ефективним, ніж nextIntпідхід, https://stackoverflow.com/a/738651/360211

Одним із стандартних схем для цього є:

Min + (int)(Math.random() * ((Max - Min) + 1))

Функція бібліотеки Java Math Math.random () генерує подвійне значення в діапазоні [0,1). Зверніть увагу, що цей діапазон не включає 1.

Щоб спочатку отримати конкретний діапазон значень, вам потрібно помножити на величину діапазону значень, який потрібно охопити.

Math.random() * ( Max - Min )

Це повертає значення в діапазоні [0,Max-Min), де "Max-Min" не включений.

Наприклад, якщо ви хочете [5,10), вам потрібно покрити п'ять цілих значень, щоб ви їх використовували

Math.random() * 5

Це поверне значення в діапазоні [0,5), де 5 не включено.

Тепер вам потрібно зрушити цей діапазон до діапазону, на який ви орієнтовані. Це ви робите, додаючи значення Min.

Min + (Math.random() * (Max - Min))

Тепер ви отримаєте значення в діапазоні [Min,Max). Наслідуючи наш приклад, це означає [5,10):

5 + (Math.random() * (10 - 5))

Але це все ще не включає, Maxі ви отримуєте подвійне значення. Для того, щоб Maxвключити значення, вам потрібно додати 1 до параметра діапазону, (Max - Min)а потім обрізати десяткову частину, відкинувши до int. Це здійснюється за допомогою:

Min + (int)(Math.random() * ((Max - Min) + 1))

І ось у вас це є. Випадкове ціле значення в діапазоні [Min,Max]або на прикладі [5,10]:

5 + (int)(Math.random() * ((10 - 5) + 1))

82
Документація Sun чітко говорить, що вам краще використовувати Random (), якщо вам потрібен int замість Math.random (), який видає дубль.
Ліліан А. Морару

6
Це насправді зміщена по порівнянні з методами nextInt stackoverflow.com/a/738651/360211
Уестон

4
"Упереджений" в даному випадку означає, що після 2 ^ 53 страт, деякі числа матимуть в середньому одну додаткову появу.
Головоногі

378

Використання:

Random ran = new Random();
int x = ran.nextInt(6) + 5;

Ціле число xтепер є випадковим числом, яке має можливий результат 5-10.



137

З вони ввели метод ints(int randomNumberOrigin, int randomNumberBound)у Randomкласі.

Наприклад, якщо ви хочете генерувати п'ять випадкових цілих чисел (або одне одне) у діапазоні [0, 10], просто зробіть:

Random r = new Random();
int[] fiveRandomNumbers = r.ints(5, 0, 11).toArray();
int randomNumber = r.ints(1, 0, 11).findFirst().getAsInt();

Перший параметр вказує просто розмір IntStreamствореного (який є перевантаженим методом того, який виробляє необмежений IntStream).

Якщо вам потрібно зробити кілька окремих дзвінків, ви можете створити з потоку нескінченний примітивний ітератор:

public final class IntRandomNumberGenerator {

    private PrimitiveIterator.OfInt randomIterator;

    /**
     * Initialize a new random number generator that generates
     * random numbers in the range [min, max]
     * @param min - the min value (inclusive)
     * @param max - the max value (inclusive)
     */
    public IntRandomNumberGenerator(int min, int max) {
        randomIterator = new Random().ints(min, max + 1).iterator();
    }

    /**
     * Returns a random number in the range (min, max)
     * @return a random number in the range (min, max)
     */
    public int nextInt() {
        return randomIterator.nextInt();
    }
}

Ви також можете зробити це для doubleі longзначення. Я сподіваюся, що це допомагає! :)


1
Я б запропонував вам створити інстанцію randomIterator лише один раз. Дивіться коментар Грега Кейса щодо власної відповіді.
Naxos84

якщо ви можете, можете пояснити, у чому полягає значення streamSize- перший параметр цього методу streamSize !=0. Яка різниця, якщо streamSizeзадано 1/2 / n?
Ерфан Ахмед

104

Ви можете змінити свій другий приклад коду:

Random rn = new Random();
int range = maximum - minimum + 1;
int randomNum =  rn.nextInt(range) + minimum;

99

Було б достатньо лише невеликої модифікації вашого першого рішення.

Random rand = new Random();
randomNum = minimum + rand.nextInt((maximum - minimum) + 1);

Дивіться більше тут для впровадження Random


Для мінімального <= значення <максимум, я зробив те ж саме з Math: randomNum = minimum + (int) (Math.random () * (максимальний-мінімум)); але кастинг не дуже приємно бачити;)
AxelH

Дублікат відповіді не менше ніж на 7 років.
Maarten Bodewes

70

ThreadLocalRandomеквівалент класу java.util.Randomдля багатопотокового середовища. Генерація випадкового числа здійснюється локально в кожній з ниток. Таким чином, ми маємо кращі результати, зменшуючи конфлікти.

int rand = ThreadLocalRandom.current().nextInt(x,y);

x, y- інтервали, наприклад (1,10)


66

Math.RandomКлас в Java є 0 на основі. Отже, якщо ви пишете щось подібне:

Random rand = new Random();
int x = rand.nextInt(10);

xбуде між 0-9включно.

Отже, враховуючи наступний масив 25елементів, код для генерування випадкового числа між 0(базовим масивом) і array.lengthбуде:

String[] i = new String[25];
Random rand = new Random();
int index = 0;

index = rand.nextInt( i.length );

Оскільки i.lengthповернеться 25, nextInt( i.length )то поверне число між діапазоном 0-24. Інший варіант - це те, Math.Randomщо працює так само.

index = (int) Math.floor(Math.random() * i.length);

Для кращого розуміння перегляньте допис на форумі " Випадкові інтервали" (archive.org) .


+1 для посилання на скріншот архіву wayBackMachine та ваша відповідь все ще корисна посилання, щоб отримати "випадкові" вставки без діапазону.
Tapper7


@CodeConfident indexЗмінна не впливатиме на результат випадкового числа. Ви можете ініціалізувати його будь-яким способом, який хочете, не турбуючись про зміну результату. Сподіваюсь, це допомагає.
Метт Р

Точно ... це абсолютно невикористано. Я б ініціалізував це безпосередньо до ранду:int index = rand.nextInt(i.Length);
AjahnCharles

Або зовсім не. int index; \n index = rand...якщо хтось захоплюється деклараціями та завданнями в різних рядках. Деякі стандарти кодування є більш суворими (і без видимої мети), ніж інші.
Марк Сторер

49

Вибачте за те, що я ставлюся до вразливих, але рішення, запропоноване більшістю, тобто min + rng.nextInt(max - min + 1)), видається небезпечним через те, що:

  • rng.nextInt(n)не може досягти Integer.MAX_VALUE.
  • (max - min)може спричинити переповнення, коли minнегативний.

Нерозумне рішення дозволить повернути правильні результати для будь-якого min <= maxв межах [ Integer.MIN_VALUE, Integer.MAX_VALUE]. Розглянемо наступне наївне здійснення:

int nextIntInRange(int min, int max, Random rng) {
   if (min > max) {
      throw new IllegalArgumentException("Cannot draw random int from invalid range [" + min + ", " + max + "].");
   }
   int diff = max - min;
   if (diff >= 0 && diff != Integer.MAX_VALUE) {
      return (min + rng.nextInt(diff + 1));
   }
   int i;
   do {
      i = rng.nextInt();
   } while (i < min || i > max);
   return i;
}

Хоча і неефективно, зауважте, що ймовірність успіху в whileциклі завжди буде 50% або вище.


Чому б не кинути IllegalArgumentException, коли різниця = Integer.MAX_VALUE? Тоді вам не потрібен цикл while.
народний депутат Корстаньє

3
@mpkorstanje Ця реалізація призначена для роботи з будь-якими значеннями min <= max, навіть коли їх різниця дорівнює або навіть більша, ніж MAX_VALUE. Запуск циклу до досягнення успіху є загальною закономірністю в цьому випадку, щоб гарантувати рівномірний розподіл (якщо основне джерело випадковості є рівномірним). Random.nextInt (int) робить це внутрішньо, коли аргумент не є силою 2.
Крістіан Семрау

44

Це можна зробити, просто зробивши заяву:

Randomizer.generate(0,10); //min of zero, max of ten

Нижче його вихідний код

Randomizer.java

public class Randomizer {
    public static int generate(int min,int max) {
        return min + (int)(Math.random() * ((max - min) + 1));
    }
}

Це просто чисто і просто.


5
Це могло бути редагуванням іншого прикладу, тепер це просто кричуща копія репутації. Вказуючи, що "відповідь з найбільшою кількістю голосів" також не дуже пряма, вона може змінитися.
Maarten Bodewes

1
Це правильно @MaartenBodewes. Коли я писав цю відповідь, відповідь з більшістю голосів вище все ще писався як алгоритмне рішення. Тепер рішення вище змінилося сильно змінилося, і тепер ця відповідь виглядала як копія-кот.
Abel Callejo

Я дійсно не розумію, чому такі основні біти коду не входять до стандартних бібліотек Java. Чому я повинен це здійснити?
Ліві Л


31

Візьмемо приклад.

Припустимо, я хочу створити число між 5-10 :

int max = 10;
int min = 5;
int diff = max - min;
Random rn = new Random();
int i = rn.nextInt(diff + 1);
i += min;
System.out.print("The Random Number is " + i);

Давайте зрозуміємо це ...

Ініціалізуйте макс. З найвищим значенням та хв з найнижчим значенням.

Тепер нам потрібно визначити, скільки можливих значень можна отримати. Для цього прикладу було б:

5, 6, 7, 8, 9, 10

Отже, рахувати це було б max - min + 1.

тобто 10 - 5 + 1 = 6

Випадкове число генерує число між 0-5 .

тобто 0, 1, 2, 3, 4, 5

Додавання мінімального значення до випадкового числа призведе до:

5, 6, 7, 8, 9, 10

Отже, ми отримуємо бажаний діапазон.



26

Створіть випадкове число для різниці min та max, використовуючи метод nextint (n), а потім додайте min результат до результату:

Random rn = new Random();
int result = rn.nextInt(max - min + 1) + min;
System.out.println(result);

26

Я використовую це:

 /**
   * @param min - The minimum.
   * @param max - The maximum.
   * @return A random double between these numbers (inclusive the minimum and maximum).
   */
 public static double getRandom(double min, double max) {
   return (Math.random() * (max + 1 - min)) + min;
 }

Якщо ви хочете, ви можете передати його цілому коду.


Ця функція створює однакове число знову і знову. У моєму випадку це було: 2147483647
сокра

Помилка: у вас функція, яка вимагає подвійного, а потім виконувати + 1? Це, безумовно, суперечить принципу найменшого здивування. Що станеться, якщо ви використовуєте min = 0,1 та max = 0,2?
Maarten Bodewes

@sokras метод викликає new Random(перевірити JavaDoc): "Створює новий генератор випадкових чисел. Цей конструктор встановлює насіння генератора випадкових чисел на величину, яке, ймовірно, відрізняється від будь-якого іншого виклику цього конструктора." Дуже ймовірно, це може просто використовувати поточний час як насіння. Якщо цей час використовує мілісекунди, то поточні комп’ютери досить швидкі, щоб генерувати те саме число. Але крім того 2147483647 є Integer.MAX_VALUE; вихід, очевидно, залежить від входу, який ви не вказали.
Maarten Bodewes

Нарешті знайшов той, що насправді працює
Jency

23

Що стосується Java 7, ви більше не повинні використовувати Random. Зараз для більшості застосувань вибраний генератор випадкових чисел ThreadLocalRandom.

Використовуйте для об'єднання вилок басейни та паралельні потоки SplittableRandom.

Джошуа Блох. Ефективна Java. Третє видання.

Починаючи з Java 8

Для пулів приєднання вилок та паралельних потоків використання, SplittableRandomяке зазвичай швидше, має кращу статистичну незалежність та властивості рівномірності порівняно з Random.

Для генерації випадкових випадків intу діапазоні[0, 1_000]:

int n = new SplittableRandom().nextInt(0, 1_001);

Для генерації випадкового int[100]масиву значень у діапазоні[0, 1_000]:

int[] a = new SplittableRandom().ints(100, 0, 1_001).parallel().toArray();

Щоб повернути Потік випадкових значень:

IntStream stream = new SplittableRandom().ints(100, 0, 1_001);

Чи є причина, чому приклад включає в себе .parallel()? Мені здається, що генерування 100 випадкових чисел було б занадто банальним, щоб навести паралелізм.
Джонбот

@Johnbot Дякую за коментар, ви праві. Але головна причина полягала в тому, щоб показати API (звичайно, розумний шлях - це вимірювання продуктивності перед використанням parallelобробки). До речі, для масиву 1_000_000елементів parallelверсія була в 2 рази швидшою на моїй машині порівняно з послідовною.
Олександр Пирохов

21

Просто використовуйте клас Random :

Random ran = new Random();
// Assumes max and min are non-negative.
int randomInt = min + ran.nextInt(max - min + 1);

8
Я не бачу тут нічого нового, що не було розміщено в незліченних попередніх дописах.
Maarten Bodewes

20

Ці методи можуть бути зручними у використанні:

Цей метод поверне випадкове число між наданим значенням min та max:

public static int getRandomNumberBetween(int min, int max) {
    Random foo = new Random();
    int randomNumber = foo.nextInt(max - min) + min;
    if (randomNumber == min) {
        // Since the random number is between the min and max values, simply add 1
        return min + 1;
    } else {
        return randomNumber;
    }
}

і цей метод поверне випадкове число із заданого значення min та max (так що згенероване число також може бути числом min або max):

public static int getRandomNumberFrom(int min, int max) {
    Random foo = new Random();
    int randomNumber = foo.nextInt((max + 1) - min) + min;

    return randomNumber;
}

// Since the random number is between the min and max values, simply add 1. Чому? Не рахується хв? Зазвичай діапазон становить [хв, макс.), Де включено min, а max виключено. Неправильна відповідь, проголосований.
Maarten Bodewes

@MaartenBodewes +1 додається, тому що getRandomNumberBetween генерує випадкове число, не враховуючи наданих кінцевих точок.
Люк Тейлор

1
Це число min + 1буде вдвічі більшим, ніж інше число в результаті getRandomNumberBetween!
Лій

19

У випадку розкочування кістки це буде випадкове число від 1 до 6 (а не 0 до 6), тож:

face = 1 + randomNumbers.nextInt(6);

19
int random = minimum + Double.valueOf(Math.random()*(maximum-minimum )).intValue();

Або подивіться на RandomUtils від Apache Commons .


Це корисно, але остерігайтеся невеликого недоліку: підписи методів виглядають як: nextDouble (подвійний startInclusive, подвійний endInclusive), але якщо ви заглянете всередину методів, endInclusive насправді повинен бути endExclusive.
zakmck

Double.valueOf(Math.random()*(maximum-minimun)).intValue()це доволі придушений (і неефективний) спосіб сказати (int)(Math.random()*(maximum-minimun))
Холгер

Орфографічна невідповідність мінімального мінімуму повернення + Double.valueOf (Math.random () * (максимальний - мінімальний)). IntValue ();
Хришикеш Мішра

18

Ось корисний клас для генерування випадкових intsвипадків у діапазоні з будь-якою комбінацією інклюзивних / ексклюзивних меж:

import java.util.Random;

public class RandomRange extends Random {
    public int nextIncInc(int min, int max) {
        return nextInt(max - min + 1) + min;
    }

    public int nextExcInc(int min, int max) {
        return nextInt(max - min) + 1 + min;
    }

    public int nextExcExc(int min, int max) {
        return nextInt(max - min - 1) + 1 + min;
    }

    public int nextIncExc(int min, int max) {
        return nextInt(max - min) + min;
    }
}

18

Щоб генерувати випадкове число "між двома числами", використовуйте такий код:

Random r = new Random();
int lowerBound = 1;
int upperBound = 11;
int result = r.nextInt(upperBound-lowerBound) + lowerBound;

Це дає вам випадкове число від 1 (включно) до 11 (виключно), тому ініціалізуйте значення верхнього зв’язку, додавши 1. Наприклад, якщо ви хочете генерувати випадкове число від 1 до 10, тоді ініціалізуйте число верхнього зв’язку з 11 замість 11 10.


18

Ви можете досягти цього стисло на Java 8:

Random random = new Random();

int max = 10;
int min = 5;
int totalNumber = 10;

IntStream stream = random.ints(totalNumber, min, max);
stream.forEach(System.out::println);

17
public static Random RANDOM = new Random(System.nanoTime());

public static final float random(final float pMin, final float pMax) {
    return pMin + RANDOM.nextFloat() * (pMax - pMin);
}

16

Ще один варіант - просто використання Apache Commons :

import org.apache.commons.math.random.RandomData;
import org.apache.commons.math.random.RandomDataImpl;

public void method() {
    RandomData randomData = new RandomDataImpl();
    int number = randomData.nextInt(5, 10);
    // ...
 }

16

Я знайшов цей приклад Створення випадкових чисел :


Цей приклад генерує випадкові цілі числа у визначеному діапазоні.

import java.util.Random;

/** Generate random integers in a certain range. */
public final class RandomRange {

  public static final void main(String... aArgs){
    log("Generating random integers in the range 1..10.");

    int START = 1;
    int END = 10;
    Random random = new Random();
    for (int idx = 1; idx <= 10; ++idx){
      showRandomInteger(START, END, random);
    }

    log("Done.");
  }

  private static void showRandomInteger(int aStart, int aEnd, Random aRandom){
    if ( aStart > aEnd ) {
      throw new IllegalArgumentException("Start cannot exceed End.");
    }
    //get the range, casting to long to avoid overflow problems
    long range = (long)aEnd - (long)aStart + 1;
    // compute a fraction of the range, 0 <= frac < range
    long fraction = (long)(range * aRandom.nextDouble());
    int randomNumber =  (int)(fraction + aStart);    
    log("Generated : " + randomNumber);
  }

  private static void log(String aMessage){
    System.out.println(aMessage);
  }
} 

Приклад запуску цього класу:

Generating random integers in the range 1..10.
Generated : 9
Generated : 3
Generated : 3
Generated : 9
Generated : 4
Generated : 1
Generated : 3
Generated : 9
Generated : 10
Generated : 10
Done.

14

Краще використовувати SecureRandom, а не просто Random.

public static int generateRandomInteger(int min, int max) {
    SecureRandom rand = new SecureRandom();
    rand.setSeed(new Date().getTime());
    int randomNum = rand.nextInt((max - min) + 1) + min;
    return randomNum;
}

1
Це не так добре, тому що якщо він буде виконаний в одну мільйонну секунду, ви отримаєте однакове число, вам потрібно поставити ініціалізацію rand і setSeet поза методом.
марака

Вам потрібно насіння, так, але за допомогою SecureRandom.
grep

Вибачте, але той, хто відхилив запит на зміну, не має жодного поняття програмування Java. Це гарна пропозиція, але як це неправильно, тому що, якщо виконано в одну мільйонну секунду, воно дасть те саме число, а не випадкове.
марака

Правильного рішення ніде не знайти, не багато людей знають статичні блоки ініціалізатора ... саме це слід використовувати для встановлення насіння: 1: private static int SecureRandom rand = new SecureRandom();2: static {3: rand.setSeed(...);4:}
maraca

5
Потрібно висівати абсолютно не потрібно SecureRandom, це буде висіяно системою. Безпосередній дзвінок setSeedдуже небезпечний, він може замінити (дійсно випадковий) насіння датою. І це, безумовно, не призведе до того SecureRandom, що хтось може вгадати час і спробувати надати свій власний SecureRandomекземпляр цією інформацією.
Maarten Bodewes

13

Ось простий зразок, який показує, як генерувати випадкове число із закритого [min, max]діапазону, покиmin <= max is true

Ви можете повторно використовувати його як поле в класі отворів, також маючи всі Random.classметоди в одному місці

Приклад результатів:

RandomUtils random = new RandomUtils();
random.nextInt(0, 0); // returns 0
random.nextInt(10, 10); // returns 10
random.nextInt(-10, 10); // returns numbers from -10 to 10 (-10, -9....9, 10)
random.nextInt(10, -10); // throws assert

Джерела:

import junit.framework.Assert;
import java.util.Random;

public class RandomUtils extends Random {

    /**
     * @param min generated value. Can't be > then max
     * @param max generated value
     * @return values in closed range [min, max].
     */
    public int nextInt(int min, int max) {
        Assert.assertFalse("min can't be > then max; values:[" + min + ", " + max + "]", min > max);
        if (min == max) {
            return max;
        }

        return nextInt(max - min + 1) + min;
    }
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.