Как оптимизировать программу с правилами перезаписи строк

У меня есть небольшая программа, которая постоянно переписывает одну и ту же строку

string a = "aRbFR";
string b = "LFaLb";
string str = "Fa";

for (; n < 50; ++n)
{
    for(Int64 i = 0; i < str.Length; ++i)
    {
        if (i < str.Length && str[(int)i] == 'a')
        {
            str = str.Remove((int)i, 1);
            str = str.Insert((int)i, a);
            i += a.Length;
        }

        if (i < str.Length && str[(int)i] == 'b')
        {
            str = str.Remove((int)i, 1);
            str = str.Insert((int)i, b);
            i += b.Length;
        }
    } 
}

Eg: n0 = "Fa", n1 = "FaRbFR", n2 = "FaRbFRRLFaLbFR"

Возможно ли многопоточность, поскольку мне нужно постоянно обновлять одну и ту же переменную? Я думал, что могу сделать свой str нестабильным, но я не могу представить, как он будет работать должным образом, поскольку всем потокам потребуется обновить одну и ту же переменную.

Как я могу сделать это быстрее? Это длилось вечно, а значение переменной все еще оставалось на довольно низком уровне n = 17.

Итак, у вас s (0) = «Fa». Тогда у вас есть s (1) = F (s (0)), и в более общем плане s (n) = F (s (n-1)). Как вы могли распараллелить это, вы не можете сделать следующий шаг, пока не закончите текущий. Если это медленно, попробуйте сделать это с помощью StringBuilder. Объединение строк в цикле - это почти всегда неправильный поступок.

Flydog57 12.01.2019 01:28

Да, я так думал. Я посмотрю на это

Greggz 12.01.2019 02:24

Но я использовал «Удалить» и «Вставить». При этом используются конкатенации?

Greggz 12.01.2019 02:34

Я предполагаю, что у вас нет петабайт ОЗУ, поэтому вам понадобится альтернативный подход. Для чего именно это нужно?

Antonín Lejsek 12.01.2019 05:43

Строковый класс неизменен. если вы измените строку, старая будет мусором, и вы укажете на новую строку, независимо от того, вставляет ли она, удаляет или объединяет

Flydog57 12.01.2019 05:47

@ AntonínLejsek Это проблема, известная как Heighway Dragon. Для моей конкретной проблемы мне нужно знать, как получится строка после 50 итераций.

Greggz 12.01.2019 13:54

Я знаю это. Но ожидаемый размер строки в петабайтах. Итак, остается вопрос: как вы думаете, зачем вам создавать такую ​​большую строку?

Antonín Lejsek 12.01.2019 15:17

@ AntonínLejsek Это был всего лишь мой взгляд на решение проблемы. Вероятно, это можно сделать с меньшими вычислительными усилиями.

Greggz 12.01.2019 15:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
90
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Прежде чем рассматривать потоковую передачу чего-либо для повышения производительности, лучше всего усовершенствовать свой алгоритм. Оказывается, это подходит для Stack, поскольку он просто создает дерево и выравнивает его. Временная сложность резко снижается.

Также обратите внимание, так как это в основном рекурсивно, а потребность в данных растет экспоненциально, даже стек будет исчерпывать память на более высоких уровнях. Вы могли бы потенциально буферизовать все это на диске, поскольку это Stack.

Еще одна проблема заключалась в распределении, так что когда-то против Stack здесь помогает, так как он растет изнутри более эффективно, как и StringBuilder.

Версия стека

public static string StackVersion(int levels, string input)
{

   var stack = new Stack<(int l, char c)>();

   void PushAllReverse(int l, string seq)
   {
      for (var index = seq.Length - 1; index >= 0; index--)
         stack.Push((l, seq[index]));
   }

   PushAllReverse(1, input);

   var sb = new StringBuilder();
   while (stack.Any())
   {
      // pop the next
      var val = stack.Pop();

      // limit the levels
      if (val.l < levels)
         // add to stack if needed
         if (val.c == 'a') PushAllReverse(val.l + 1, a);
         else if (val.c == 'b') PushAllReverse(val.l + 1, b);
         else sb.Append(val.c); // append the char
      else
         // level limit, just append
         if (val.c == 'a') sb.Append(a);
         else if (val.c == 'b') sb.Append(b);
         else sb.Append(val.c);
   }

   return sb.ToString();
}

Оригинал

public static string OriginalVersion(int levels, string input)
{
   for (int n = 0; n < levels; n++)
   {
      for (var i = 0; i < input.Length; i++)
      {
         if (i < input.Length && input[i] == 'a')
         {
            input = input.Remove(i, 1);
            input = input.Insert(i, a);
            i += a.Length;
         }

         if (i < input.Length && input[i] == 'b')
         {
            input = input.Remove(i, 1);
            input = input.Insert(i, b);
            i += b.Length;
         }
      }
   }

   return input;
}

Сроки тестирования (итерации)

Stack : (5) 00:00:00.0000111
Orig  : (5) 00:00:00.0000105

Stack : (6) 00:00:00.0000228
Orig  : (6) 00:00:00.0000318

Stack : (7) 00:00:00.0000483
Orig  : (7) 00:00:00.0001065

Stack : (8) 00:00:00.0000621
Orig  : (8) 00:00:00.0003524

Stack : (9) 00:00:00.0001143
Orig  : (9) 00:00:00.0014589

Stack : (10) 00:00:00.0002284
Orig  : (10) 00:00:00.0022475

Stack : (11) 00:00:00.0003179
Orig  : (11) 00:00:00.0092901

Stack : (12) 00:00:00.0006805
Orig  : (12) 00:00:00.0222648

Stack : (13) 00:00:00.0013283
Orig  : (13) 00:00:00.0920365

Stack : (14) 00:00:00.0036728
Orig  : (14) 00:00:00.4287529

Stack : (15) 00:00:00.0056583
Orig  : (15) 00:00:01.5850379

Пример результата 5 итераций (поскольку изначально я ошибся, лучше выложу результаты)

Куча

FaRbFRRLFaLbFRRLFaRbFRLLFaLbFRRLFaRbFRRLFaLbFRLLFaRbFRLLFaLbFRRLFaRbFRRLFaLbFRRLFaRbFRLLFaLbFRLLFaRbFRRLFaLbFRLLFaRbFRLLFaLbFR

Оригинал

FaRbFRRLFaLbFRRLFaRbFRLLFaLbFRRLFaRbFRRLFaLbFRLLFaRbFRLLFaLbFRRLFaRbFRRLFaLbFRRLFaRbFRLLFaLbFRLLFaRbFRRLFaLbFRLLFaRbFRLLFaLbFR

Спасибо за Ваш ответ. Это действительно быстрее! Но когда он был на 50 уровне, это вызвало у меня исключение из-за нехватки памяти! Мне нужно пройти 50 уровень. Есть еще идеи?

Greggz 12.01.2019 13:58

Это разбивает около 3,5 ГБ памяти. Поэтому я не могу вызвать на toString().

Greggz 12.01.2019 14:29

Возможно, когда я выйду из метода, stack будет бесплатным и даст мне еще немного места для программы

Greggz 12.01.2019 14:31

Код работает некорректно для уровней == 0. Я бы не сказал, что это ошибка, но это стоит знать.

Antonín Lejsek 12.01.2019 16:54

@Greggz .net имеет жесткие ограничения на размер строки, в зависимости от архитектуры 32 или 64

TheGeneral 12.01.2019 17:22

Генерал прав, он просто пересекает дерево. Вы делаете это с помощью стека, но я предпочитаю не явный стек, а неявный стек, также известный как рекурсия.

protected void makeA(int level, StringBuilder sb)
{
    if (level == 0)
    {
        sb.Append('a');
    }
    else
    {
        makeA(level - 1, sb);
        sb.Append("R");
        makeB(level - 1, sb);
        sb.Append("FR");
    }
}

protected void makeB(int level, StringBuilder sb)
{
    if (level == 0)
    {
        sb.Append('b');
    }
    else
    {
        sb.Append("LF");
        makeA(level - 1, sb);
        sb.Append("L");
        makeB(level - 1, sb);
    }
}

protected string makeString(int level)
{
    StringBuilder sb = new StringBuilder();
    sb.Append("F");
    makeA(level, sb);
    return sb.ToString();
}

Код более простой и примерно в пять раз быстрее, чем версия TheGeneral. Но это не имеет особого значения. Строка для 50 итераций была бы слишком большой. Эти два значения равны:

double c1 = makeString(i).Count(c => c == 'a' || c == 'b');
double c2 = Math.Pow(2, i);

И 2 ^ 50 = 1125899906842624, то есть 2,2 ПБ только для этих двух символов (1 символ si 2 байта).

Да, я вижу, насколько это быстрее, но это не касается других комбинаций, в любом случае проголосуйте за отличную работу.

TheGeneral 12.01.2019 17:20

Так, по вашему мнению, невозможно рассчитать весь «путь» на моей машине? Файл .txt со всем путем будет иметь размер ~ 1 ТБ, не так ли?

Greggz 12.01.2019 18:03

Его можно рассчитать (примерное время расчета - около года), но невозможно сохранить на обычном компьютере. Файл с результатом будет размером в несколько петабайт. 1ПБ = 1000ТБ. Все это с самого начала кажется проблемой XY meta.stackexchange.com/questions/66377/what-is-the-xy-proble‌ m

Antonín Lejsek 12.01.2019 18:33

Это не проблема XY. Если вы повторно ознакомились с проблемой, «F», «R», «L» - это инструкции, которым я должен следовать, чтобы найти координаты. И мне нужно прочитать 10 ^ 12 "F" (шагов). Итак, я пытался найти способ сохранить информацию, но теперь я знаю, что это невозможно.

Greggz 12.01.2019 23:35

Затем я обнаружил, что есть уравнение для координат после удвоения значения шагов. Итак, я только что рассчитал значение для 244140625 шагов. А затем использовал уравнение 11 раз, пока не достиг 10 ^ 12 шагов.

Greggz 12.01.2019 23:50

И мы к чему-то приближаемся. Вы пытаетесь решить этот projecteuler.net/problem=220, верно? Вам вообще не нужно создавать какую-либо строку, вместо этого вы можете выполнять движение, так что для этого почти не потребуется памяти. И вам точно не нужен весь D50. По моим оценкам, вы можете смоделировать 10 ^ 12 шагов примерно за 5 часов. Но формула - это умный ярлык.

Antonín Lejsek 13.01.2019 04:42

@ AntonínLejsek Ага, старался и добился успеха благодаря вам, ребята. Да, я видел, что после того, как я проверил все решения, намного лучше, чем мое собственное, я записал 4 ГБ в текстовый файл lol (n = 30 содержит 10 ^ 12 шагов). Да, формула была интересной уловкой, которую я сделал после того, как понял, что невозможно сохранить информацию, должен быть какой-то отпечаток пальца на пути, который действительно существовал.

Greggz 13.01.2019 05:27

Другие вопросы по теме