Рубрики
Без рубрики

42: Ответ На Жизнь, Вселенную и Все Остальное

История началась с сообщения в Твиттере о методе JDK bitCount, который доступен в течение длительного времени и… С тегами программирование, java, jdk.

История началась с Сообщения в Твиттере о методе JDK количество битов которое доступно для Длинный и Целое число

Если вы посмотрите на временную шкалу, там был ответ https://twitter.com/ScottSelikoff/status/1254185742760280064 |/by @scottselikoff (funny comment of course) who stated 42 as the answer to life, the universe and everything to the magic method which has been followed by//Лукас Эдер :

Для какого процента лонгов это был бы правильный результат?

Эта тема вдохновила меня на эту статью.

Итак, давайте подытожим этот вопрос:

Для какого процента длинных значений количество битов() ?

Первый вопрос, который возникает: что это считается делает? Давайте взглянем на javadoc функции (целочисленный вариант):

Возвращает количество однобитовых в двоичном представлении, дополняющем два, указанного значения int. Эту функцию иногда называют подсчетом численности населения.

Так что в конце концов bitCount подсчитывает количество (как следует из названия) 1 s, которые находятся в заданном значении.

Вот некоторые примерные значения для типа Целое число :

  • Целое число Количество бит=2
  • Целое число Количество бит=2
  • Целое число Количество бит=4
  • Целое число |/Количество битов=12 Целое число
  • |/Количество битов=12 Целое число |/Количество бит=32

Очень наивным способом попытаться решить этот вопрос было бы написать такой код:

BigDecimal pow = BigDecimal.valueOf(2L).pow(64);

Map collect = LongStream.range(Long.MIN_VALUE, Long.MAX_VALUE)
  .boxed()
  .collect(groupingBy(Long::bitCount, counting()));

collect.forEach((k, v) -> System.out.printf("k = %4s v=%15s %7.5f\n", k, v, BigDecimal.valueOf(v)
  .divide(pow)
  .multiply(BigDecimal.valueOf(100L))));

В первой строке будет создано 2^64 , равное 100%, и BigDecimal.valueOf(v).разделить(pow).умножить(BigDecimal.valueOf(100L)) просто вычислит процент значения (количество значений для соответствующего количества битов) результирующей карты, которая содержит число.

Хорошо, теперь мы можем попробовать это…. Действительно? Никогда. Этот код будет выполняться очень и очень долго…

Давным-давно в далекой-далекой галактике…

Это может быть немного долгим ожиданием результата. Хорошо, давайте немного подумаем о коде. Ах! Да, конечно, я понял. Мы должны использовать parallel() для потока, чтобы ускорить его.

BigDecimal pow = BigDecimal.valueOf(2L).pow(64);

Map collect = LongStream.range(Long.MIN_VALUE, Long.MAX_VALUE)
  .boxed()
  .parallel()
  .collect(groupingBy(Long::bitCount, counting()));

collect.forEach((k, v) -> System.out.printf("k = %4s v=%15s %7.5f\n", k, v, BigDecimal.valueOf(v)
  .divide(pow)
  .multiply(BigDecimal.valueOf(100L))));

Эта версия будет быстрее предыдущей и займет… Извините но я просто не знаю, потому что я не проверял это;-).

Можем ли мы сделать более простое и быстрое решение, чтобы в конечном итоге получить результат? Хорошо, мы изменим Длинный в Целое число ? Теперь код выглядит так. Как вы видите, я уже добавил параллель() в коде для ускорения также вы видите, что я использую BigDecimal.valueOf(2L).pow(32) (2^32) вместо 2^64 на основе использования Целого числа :

BigDecimal pow = BigDecimal.valueOf(2L).pow(32);

Map collect = IntStream.range(Integer.MIN_VALUE, Integer.MAX_VALUE)
  .boxed()
  .parallel()
  .collect(groupingBy(Integer::bitCount, counting()));

collect.forEach((k, v) -> System.out.printf("k = %4s v=%15s %7.3f\n", k, v, BigDecimal.valueOf(v)
  .divide(pow)
  .multiply(BigDecimal.valueOf(100L))));

Таким образом, этот код будет выполняться в ca. 15 секунд (шестиядерный процессор) со следующим результатом:

k =    0 v=              1   0.000
k =    1 v=             32   0.000
k =    2 v=            496   0.000
k =    3 v=           4960   0.000
k =    4 v=          35960   0.001
k =    5 v=         201376   0.005
k =    6 v=         906192   0.021
k =    7 v=        3365856   0.078
k =    8 v=       10518300   0.245
k =    9 v=       28048800   0.653
k =   10 v=       64512240   1.502
k =   11 v=      129024480   3.004
k =   12 v=      225792840   5.257
k =   13 v=      347373600   8.088
k =   14 v=      471435600  10.976
k =   15 v=      565722720  13.172
k =   16 v=      601080390  13.995
k =   17 v=      565722720  13.172
k =   18 v=      471435600  10.976
k =   19 v=      347373600   8.088
k =   20 v=      225792840   5.257
k =   21 v=      129024480   3.004
k =   22 v=       64512240   1.502
k =   23 v=       28048800   0.653
k =   24 v=       10518300   0.245
k =   25 v=        3365856   0.078
k =   26 v=         906192   0.021
k =   27 v=         201376   0.005
k =   28 v=          35960   0.001
k =   29 v=           4960   0.000
k =   30 v=            496   0.000
k =   31 v=             31   0.000
k =   32 v=              1   0.000

Первый столбец k= – это количество битов итак, первая строка означает:

У нас есть одно значение v=1 где количество битов==0 и 0.000 процентов.

Позвольте use принимать значение в строке для к=16 и v=601,080,390 и 13,995 процент от значений целого числа. Таким образом, это означает, что для bitCount=16 у нас есть 601,080,390 ценности, которые являются 13,995 процент от всех целых значений.

Одно интересное наблюдение, которое можно сделать здесь, заключается в том, что количество значений увеличивается до максимального значения в к=16 ( bitCount=16 ), чего вы, возможно, не ожидали. Другое дело, что количество значений уменьшается до большего числа с bitCount=32 и т.д.

Основываясь на времени выполнения этого примера, вы можете примерно оценить время выполнения для варианта с длиной: 15 с *.424.509.440 секунд/86400 секунд/.654 дня/365 дней/год , что составляет около 2000 лет. Таким образом, имея машину, на которой в 1000 раз больше процессоров, ее можно было бы сократить примерно до 2 лет (теоретически), или, возможно, вы могли бы использовать больше мощности, используя графические процессоры в облаке AWS, но, в конце концов, не было бы осуществимого решения.

Поэтому нам нужно пересмотреть, есть ли более быстрое или простое решение для ответа на этот вопрос? Да, такой есть.

Ответ можно найти, используя некоторые математические .

Давайте возьмем известный пример из приведенного выше вывода результата:

У нас есть 32 бита (целое число). Теперь у нас есть 16 бит, которые должны быть 0 и 16 бит, которые должны быть 1 . Выражая это вот так:

Вы можете рассчитать это с помощью Вольфрам Альфа и результат таков: 601080390 это точное число в приведенном выше примере. Давайте проверим некоторые другие значения:

Результирующее значение будет два раза в таблице один для к=9 ( Количество бит=9 ) и для k=23 ( ( количество битов=23 ). Значение bitCount=9 означает 9 1 с ((и 23 0 s) находятся в целочисленном значении и количество битов=23 означает 23 1 s (и 9 0

Таким образом, основываясь на математике, мы действительно могли бы ответить на этот вопрос с помощью:

Таким образом, это означает, что у нас есть 80 347 448,443,237,920 чисел, где количество битов=42 и это означает

Так что 0,435 процент всех длинных значений, имеющих количество битов=42 .

Оригинал: “https://dev.to/khmarbaise/42-the-answer-to-life-the-universe-and-everything-1npi”