2017-01-10 11 views
0

Мне нужно моделировать частицы, расщепляющиеся на частицы. Моя функция имитации решает, сколько частиц оторвалось от раскола. Затем вызывает себя для имитации в зависимости от того, сколько частиц он решил разбить.Как сделать свою рекурсивную функцию прочитанной каждый новый экземпляр, прежде чем переходить на следующий уровень цикла?

Вот код:

void Reaction::Simulate(double energy, TwoVector position) 
{ 
    int RandomNumber= qrand() %3+1;//starting particle 
    energy = energy/RandomNumber; // change energy for each reaction 
    double time=1.0; 
    std::cout<<RandomNumber<<std::endl; 

    if (RandomNumber==1){ 
     LightParticle* i=new LightParticle(energy, position); 
     int speed = 3; 
     i->SimulatePath(time, speed, i->GetPosition()); 
     i->GetPosition().Print(); 
     energy--; 
     Simulate(energy, i->GetPosition()); 
    } 
    else if (RandomNumber==2){ 
     MediumParticle* j=new MediumParticle(energy, position); 
     MediumParticle* k=new MediumParticle(energy, position); 
     int speed = 2; 

     j->SimulatePath(time,speed, position); 
     k->SimulatePath(time,speed, position); 

     j->GetPosition().Print(); 
     k->GetPosition().Print(); 

     Simulate(energy, j->GetPosition()); 
     Simulate(energy, k->GetPosition()); 
    } 
    else if (RandomNumber==3) { 
     HeavyParticle* l = new HeavyParticle(energy, position); 
     HeavyParticle* m = new HeavyParticle(energy, position); 
     HeavyParticle* n = new HeavyParticle(energy, position); 
     int speed = 1; 

     l->SimulatePath(time,speed, position); 
     l->GetPosition().Print(); 

     m->SimulatePath(time,speed, position); 
     m->GetPosition().Print(); 

     n->SimulatePath(time,speed, position); 
     n->GetPosition().Print(); 

     Simulate(energy, l->GetPosition()); 
     Simulate(energy, m->GetPosition()); 
     Simulate(energy, n->GetPosition()); 
    } 
    else return; 
} 

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

+1

Для будущих вопросов, пожалуйста, убедитесь, что ваш код правильно отформатирован. Некорректно отформатированный код чрезвычайно трудно анализировать и, следовательно, крайне сложно отлаживать. – Xirema

+0

wow, который выглядит так аккуратно, спасибо –

+3

Возможно, было бы лучше представить все частицы в структуре, такие как 'std :: vector', а затем запустить' Simulate' на все то для одной итерации и повторить столько раз как вы считаете нужным. Избегайте «новых». С помощью вектора вы просто используете 'push_back' для добавления каждой новой частицы. – wally

ответ

4

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

Ваша основная codepath выглядит следующим образом, в псевдокоде:

func() { 
    path = RAND(1, 3); 
    if(path == 1) /*DO STUFF*/ func(); 
    else if(path == 2) /*DO STUFF*/ func(); 
    else /*DO STUFF*/ func(); 
} 

Таким образом, независимо от этих шагов, являются ли «одновременное» или нет, ваш код никогда не заканчивается в любом случае. Если вы хотите, чтобы код в конечном итоге остановился, вы должны иметь случайное число в диапазоне, который не только выводит числа [1, 3].

int RandomNumber= qrand() %4; //Will stop when RandomNumber == 0 

Если есть более явное условие, когда рекурсия следует прекратить (например, когда energy == 0), вам нужно закодировать, что вместо этого.

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

Вам нужно написать что-то вроде этого:

std::thread t1([=]{ 
    LightParticle* i=new LightParticle(energy, position); 
    int speed = 3; 
    i->SimulatePath(time, speed, i->GetPosition()); 
    i->GetPosition().Print(); 
    energy--; 

    if(condition_to_continue_recursing()) { 
     Simulate(energy, i->GetPosition()); 
    } 
}); 
std::thread t2([=]{ 
    MediumParticle* j=new MediumParticle(energy, position); 
    MediumParticle* k=new MediumParticle(energy, position); 
    int speed = 2; 

    j->SimulatePath(time,speed, position); 
    k->SimulatePath(time,speed, position); 

    j->GetPosition().Print(); 
    k->GetPosition().Print(); 

    if(condition_to_continue_recursing()) { 
     Simulate(energy, j->GetPosition()); 
     Simulate(energy, k->GetPosition()); 
    } 
}); 
std::thread t3([=]{ 
    HeavyParticle* l = new HeavyParticle(energy, position); 
    HeavyParticle* m = new HeavyParticle(energy, position); 
    HeavyParticle* n = new HeavyParticle(energy, position); 
    int speed = 1; 

    l->SimulatePath(time,speed, position); 
    l->GetPosition().Print(); 

    m->SimulatePath(time,speed, position); 
    m->GetPosition().Print(); 

    n->SimulatePath(time,speed, position); 
    n->GetPosition().Print(); 

    if(condition_to_continue_recursing()) { 
     Simulate(energy, l->GetPosition()); 
     Simulate(energy, m->GetPosition()); 
     Simulate(energy, n->GetPosition()); 
    } 
}); 

t1.join(); 
t2.join(); 
t3.join(); 

Но независимо от того condition_to_continue_recursing() это придется решать вам; Я не знаю достаточно о вашей общей задаче, чтобы ответить на это. Кроме того, это порождает абсурдное количество потоков, если ваш condition_to_continue_recursing весьма значителен; использование пула потоков может быть предпочтительным. И все это зависит от того, решите ли вы, что использование потоков идеально подходит для такого рода задач, что для меня не очевидно (для меня).

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

HeavyParticle* l = new HeavyParticle(energy, position); 
HeavyParticle* m = new HeavyParticle(energy, position); 
HeavyParticle* n = new HeavyParticle(energy, position); 

Каждый из этих указателей будет течь. Так как объекты используются только в пределах, в которых они были определены, использование std::unique_ptr, вероятно, идеально:

std::unique_ptr<HeavyParticle> l = std::make_unique<HeavyParticle>(energy, position); 
std::unique_ptr<HeavyParticle> m = std::make_unique<HeavyParticle>(energy, position); 
std::unique_ptr<HeavyParticle> n = std::make_unique<HeavyParticle>(energy, position); 

EDIT: С другой стороны, нет никакой реальной хорошей причины, почему вы должны использовать указатели в этом контекста в первую очередь. Следующий код будет работать прекрасно без каких-либо утечек памяти или изменения функциональности вашего кода:

HeavyParticle l(energy, position); 
HeavyParticle m(energy, position); 
HeavyParticle n(energy, position); 
int speed = 1; 

l.SimulatePath(time,speed, position); 
l.GetPosition().Print(); 

m.SimulatePath(time,speed, position); 
m.GetPosition().Print(); 

n.SimulatePath(time,speed, position); 
n.GetPosition().Print(); 

Simulate(energy, l.GetPosition()); 
Simulate(energy, m.GetPosition()); 
Simulate(energy, n.GetPosition()); 

Вы также, вероятно, не следует использовать qrand.

std::default_random_engine engine(std::random_device()()); 

void Reaction::Simulate(double energy, TwoVector position) 
{ 
    std::uniform_int_distribution<int> distribution(0, 3); 
    int RandomNumber = distribution(engine); 
    /*... Whatever*/ 

Лучшая конструкция передаст двигатель в функцию.

Альтернатива:

std::default_random_engine engine(std::random_device()()); 

void Reaction::Simulate(double energy, TwoVector position) 
{ 
    if(energy <= 0) return; 
    std::uniform_int_distribution<int> distribution(1, 3); 
    int RandomNumber = distribution(engine); 
    /*... Whatever*/ 

Там намного больше к этому, но есть много, чтобы копаться в. Надеюсь, это даст хорошую отправную точку.

+1

Вы видите причину использования указателей вместо самих объектов? Для представленного фрагмента нет «Частицы i (энергия, положение)», «так же хорошо? Затем пусть деструктор очистит все. Отличный ответ. – Matt

+0

Одна из веских причин может заключаться в том, что объекты '* Particle' чрезвычайно тяжелые/большие, что может привести к проблемам с переполнением стека, просто выделив слишком много из них в стеке. Конечно, это очень сложный случай, и я сомневаюсь, что это проблема для кода, взятого здесь, но в определенный момент этого пути мы перестали говорить «вот конкретные проблемы с вашим кодом» и начали говорить: хорошо, я собираюсь полностью переписать это так, как я бы это сделал », что является большим расходом моего времени. = D – Xirema

+0

. Как хороший ответ, так как вопрос может попасть в его состояние. Может быть сделан случай хранения выделенных частиц в древовидной структуре, возвращения корня, а затем итерации через дерево для обработки результатов до их окончательного уничтожения.Дерево можно сделать так, чтобы * смотреть * одновременно с широким обрывом в одном потоке и одновременно обрабатывать утечку памяти. – user4581301

0

qsrand() всегда будет иметь такое же значение, если оно не инициализировано значением.

qsrand(time(NULL)); 
ID=qrand() % 3; 
qDebug << “ID random: “ << ID; 

или

qsrand(static_cast<quint64>(QTime::currentTime().msecsSinceStartOfDay())); 
array<int, 3> arr = {qrand(), qrand(), qrand()}; 
for(auto i : arr) 
    cout << i << endl; 
+3

Из-за нехватки MCVE мы не знаем, посеял ли Raket Makhim rng. – user4581301

+0

Да, я узнал, что это трудный путь. Но высевали его сейчас. Спасибо за ответ! –

0

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

void Reaction::Simulate(double energy, TwoVector position) 
{ 
    if (energy < minimalEnergy) return; 
    if (isAtGround(position)) return; 

    int RandomNumber= qrand() %3+1;//starting particle 
    // all the rest, but please repair the memory leaks! 
} 

где вы должны предоставить функцию bool isAtGround(const TwoVector& position) и minimalEnergy, когда ваша частица не может в дальнейшем разрыв (типичные пределы легчайшие массы частиц). Возможно, вместо того, чтобы возвращаться, если энергия слишком мала, вы можете следовать по пути частиц к земле. Но это зависит от вашей конкретной задачи.

Кстати, мне кажется странным, что энергия снижается только для RandomNumber==1.

+0

Да, мне тоже было сложно получить петлю, чтобы сломать. У меня есть if (энергия> 4) перед всем кодом и думал, что это прекратит имитировать частицы. Энергия снижается только на 1, потому что при 2 и 3 энергия делится поровну между новыми частицами. Спасибо за ответ –

Смежные вопросы