У меня есть небольшая программа, которая постоянно переписывает одну и ту же строку
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.
Да, я так думал. Я посмотрю на это
Но я использовал «Удалить» и «Вставить». При этом используются конкатенации?
Я предполагаю, что у вас нет петабайт ОЗУ, поэтому вам понадобится альтернативный подход. Для чего именно это нужно?
Строковый класс неизменен. если вы измените строку, старая будет мусором, и вы укажете на новую строку, независимо от того, вставляет ли она, удаляет или объединяет
@ AntonínLejsek Это проблема, известная как Heighway Dragon. Для моей конкретной проблемы мне нужно знать, как получится строка после 50 итераций.
Я знаю это. Но ожидаемый размер строки в петабайтах. Итак, остается вопрос: как вы думаете, зачем вам создавать такую большую строку?
@ AntonínLejsek Это был всего лишь мой взгляд на решение проблемы. Вероятно, это можно сделать с меньшими вычислительными усилиями.





Прежде чем рассматривать потоковую передачу чего-либо для повышения производительности, лучше всего усовершенствовать свой алгоритм. Оказывается, это подходит для 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 уровень. Есть еще идеи?
Это разбивает около 3,5 ГБ памяти. Поэтому я не могу вызвать на toString().
Возможно, когда я выйду из метода, stack будет бесплатным и даст мне еще немного места для программы
Код работает некорректно для уровней == 0. Я бы не сказал, что это ошибка, но это стоит знать.
@Greggz .net имеет жесткие ограничения на размер строки, в зависимости от архитектуры 32 или 64
Генерал прав, он просто пересекает дерево. Вы делаете это с помощью стека, но я предпочитаю не явный стек, а неявный стек, также известный как рекурсия.
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 байта).
Да, я вижу, насколько это быстрее, но это не касается других комбинаций, в любом случае проголосуйте за отличную работу.
Так, по вашему мнению, невозможно рассчитать весь «путь» на моей машине? Файл .txt со всем путем будет иметь размер ~ 1 ТБ, не так ли?
Его можно рассчитать (примерное время расчета - около года), но невозможно сохранить на обычном компьютере. Файл с результатом будет размером в несколько петабайт. 1ПБ = 1000ТБ. Все это с самого начала кажется проблемой XY meta.stackexchange.com/questions/66377/what-is-the-xy-proble m
Это не проблема XY. Если вы повторно ознакомились с проблемой, «F», «R», «L» - это инструкции, которым я должен следовать, чтобы найти координаты. И мне нужно прочитать 10 ^ 12 "F" (шагов). Итак, я пытался найти способ сохранить информацию, но теперь я знаю, что это невозможно.
Затем я обнаружил, что есть уравнение для координат после удвоения значения шагов. Итак, я только что рассчитал значение для 244140625 шагов. А затем использовал уравнение 11 раз, пока не достиг 10 ^ 12 шагов.
И мы к чему-то приближаемся. Вы пытаетесь решить этот projecteuler.net/problem=220, верно? Вам вообще не нужно создавать какую-либо строку, вместо этого вы можете выполнять движение, так что для этого почти не потребуется памяти. И вам точно не нужен весь D50. По моим оценкам, вы можете смоделировать 10 ^ 12 шагов примерно за 5 часов. Но формула - это умный ярлык.
@ AntonínLejsek Ага, старался и добился успеха благодаря вам, ребята. Да, я видел, что после того, как я проверил все решения, намного лучше, чем мое собственное, я записал 4 ГБ в текстовый файл lol (n = 30 содержит 10 ^ 12 шагов). Да, формула была интересной уловкой, которую я сделал после того, как понял, что невозможно сохранить информацию, должен быть какой-то отпечаток пальца на пути, который действительно существовал.
Итак, у вас s (0) = «Fa». Тогда у вас есть s (1) = F (s (0)), и в более общем плане s (n) = F (s (n-1)). Как вы могли распараллелить это, вы не можете сделать следующий шаг, пока не закончите текущий. Если это медленно, попробуйте сделать это с помощью
StringBuilder. Объединение строк в цикле - это почти всегда неправильный поступок.