Блог пользователя jaguar1996

Автор jaguar1996, история, 8 лет назад, По-русски

Всем привет) У меня возник вопрос, каким образом лучше выбрать приоритет в декартовом дереве, после задачи, которая падала по времени на 12 тесте, где приоритет выбирался по такой формуле:

ll prior = (rand() << 15) ^ (rand() << 15);

Задача зашла, когда приоритет считался вот так, без какого либо рандома)

const ll M = 10000000001230000000; prior = (x << 16) ^ (M*x);

После решения еще одной задачи, получилось наоборот, вторая формула давала тл, а первая зашла. Хотелось бы посмотреть как вы решаете проблему с приоритетами)

  • Проголосовать: нравится
  • +6
  • Проголосовать: не нравится

»
8 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится

ll prior = (rand() << 16)|(rand());

»
8 лет назад, # |
  Проголосовать: нравится +11 Проголосовать: не нравится
int rnd() {
    static int x = 0;
    return (x = (x * 1103515245 + 12345) & 0x7FFFFFFF);
}

По моим замерам, работает в четыре-пять раз быстрее rand.

»
8 лет назад, # |
  Проголосовать: нравится +39 Проголосовать: не нравится

изучайте C++ STL, там все написано

#include <random>
#include <chrono>

std::mt19937_64 rnd(std::chrono::system_clock::now().time_since_epoch().count());

генерирует сразу 64-битные числа, работает быстрее rand()

  • »
    »
    8 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    Интересно узнать, у человека падало из-за того что рандом плохой (дерево плохо балансировалось), или слишком медленный. Мне, например, не понятно к чему такая "криптоустойчивость". Вариант выше от SirNickolas кажется намного быстрее.

    • »
      »
      »
      8 лет назад, # ^ |
      Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

      дело не в криптоустойчивости, а в том что это единственный стандартный гсч в <random>, который выдает 64-битные числа. Судя по тестам в запуске, он выдает 107 чисел за 120 мс, т.ч. не вижу никаких проблем со скоростью.

      Если хочется именно линейный конгруэнтный гсч, то можно либо использовать стандартные 32-битные реализации (minstd_rand, minstd_rand0), либо написать что-нибудь свое как-то так:

      #include <random>
      
      typedef std::linear_congruential_engine<uint_fast32_t, 1103515245, 12345, 1UL << 31> sirnickolas_rand; 
      
      sirnickolas_rand rnd(0);
      

      (должно выводить такие же числа как код SirNickolas, не считая signed integer overflow)

  • »
    »
    6 лет назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится

    Проблема в том, что не все тестирующие системы принимают mt19937_64.

»
6 лет назад, # |
  Проголосовать: нравится +29 Проголосовать: не нравится

Проблема не во времени работы rand(), а в том, что написана какая-то ерунда — зачем оба rand() сдвигать на 15 и ксорить? Если тестирование под виндой, то у вас получается 32 тысячи различных приоритетов, декартово дерево будет бамбучить или вообще циклиться. Если под линуксом, то тоже странная конструкция — можно пользоваться обычным rand(). Ну или любым из вариантов, уже предложенных выше.

  • »
    »
    6 лет назад, # ^ |
      Проголосовать: нравится +29 Проголосовать: не нравится

    декартово дерево будет бамбучить или вообще циклиться

    циклиться

    А можно немного подробнее, как такое бывает?

»
6 лет назад, # |
  Проголосовать: нравится +9 Проголосовать: не нравится

Нужно сдвигать только один из rand().

p.s всегда пишу rand() ^ (rand() << 15)

  • »
    »
    6 лет назад, # ^ |
      Проголосовать: нравится +7 Проголосовать: не нравится

    Тоже не самая надёжная конструкция. rand() возвращает int, а при сдвиге на 15 это может уже не влезать в int. Это undefined behavior

    • »
      »
      »
      6 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

      rand() возвращает числа в диапазоне от 0 до 2^15 — 1, так что можно.

      • »
        »
        »
        »
        6 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Хотя это зависит от константы RAND_MAX, но вроде по умолчанию она 2^15 — 1.

        • »
          »
          »
          »
          »
          6 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          У меня локально (linux) RAND_MAX = 2^31 — 1. На Windows может быть действительно он 2^15 — 1, но полагаться на это явно не стоит.

          • »
            »
            »
            »
            »
            »
            6 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится

            Ну если так, тогда можно юзать просто rand() без всяких сдвигов.

            • »
              »
              »
              »
              »
              »
              »
              6 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится

              Можно. Но это опять неуниверсальное решение и его работоспособность зависит от константы RAND_MAX, которая в разных местах разная. Лучше будет использовать генераторы с фиксированной битностью (например, mt19937), либо правильно кастовать к unsigned/long long, чтобы не происходило UB.

              • »
                »
                »
                »
                »
                »
                »
                »
                6 лет назад, # ^ |
                  Проголосовать: нравится +8 Проголосовать: не нравится

                Если говорить о более универсальных решениях, то можно (если писать на массиве, а не на указателях) присвоить всем ячейкам их порядковый номер, а затем пошафлить. К тому же, вместо лонл лонгов будут инты.

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  6 лет назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится

                  А каким рандом шаффлить?

                  Мне лень искать, но тут была статья, что стандартный рандом шаффлит также плохо (по тем же самым причинам) и примерно так написанная декартка не заходила.

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  6 лет назад, # ^ |
                    Проголосовать: нравится -8 Проголосовать: не нравится

                  Можно написать взять любой хороший рандом и сделать свой random_shuffle.

                  for (int i = 0; i < n; ++i) {
                      swap(a[i], a[rand() % (i + 1)]);
                  }
                  

                  Другой способ заключается в использовании shuffle. http://www.cplusplus.com/reference/algorithm/shuffle/

                  Но честно, я не сталкивался с такими случаями, когда стандартный random_shuffle давал TL.

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  6 лет назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится