За время обучения не раз задавался вопросом: все эти хорошие и крутые алгоритмы чем могут помочь на практике? И вот, пару дней назад столкнулся с задачей по расчету цепи электрического тока. То, как это решилось и чем — под катом.



Скажу сразу, что ниже описана не полная версия алгоритма, о чем допишу в следующей части.

?Постановка задачи


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

На первый раз ограничимся 4 элементами: резистором, батарейкой, лампочкой и пустышкой, которая соединяет участок цепи (без внутреннего сопротивления). Теперь, когда задача более/менее поставлена, можно приступать к реализации.

? Внутренние обозначения


Для определенности обозначим 0 за пустышку, 1 за резистор в 200 ом, 2 за 500 ом и 3 за 1к ом. Минус означает пробел. Батарейку и лампочку поставим заранее в точках 1,2 и 5,2 соответственно:



?Чтение файла


Так как имя файла не константа, а алгоритм нужно сделать универсальным, то имя файла будет передаваемым параметром.

Вид уровня, хранящего с файле:

0 0 2 0 0
0 — - — 0
0 0 0 0 0

Считываем построчно (значит через массив строк) и разбираем потом отдельно каждую строку. Результат для каждого пустого места в плате вписываем в PlayerPrefs, чтобы можно было работать с элементами вне зависимости от исходной программы чтения из файла.

Код для чтения из файла
public string name;
private string [] s;
private string str;
void Start()
{
s=System.IO.File.ReadAllLines (name);
r=s.Length;
i = 0;
while (i<s.Length) {
str=s[i];
j=0;
while(j<str.Length)
{
if(str[j]=='-') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),-1);
if(str[j]=='0') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),0);
if(str[j]=='1') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),1);
if(str[j]=='2') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),2);
if(str[j]=='3') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),3);
j++;j++;
}
i++;
}
}

?Алгоритм обхода


Вот тут придется сделать оговорку. Так как работа со стеком вызвала некоторые сложности, реализация через очереди и стеки рассмотрю во второй части. Сейчас будем разбирать случай, когда нет никакого разветвления (крайне ограниченно, но для размеров 5х3 самое то).

Зададим начальное место обхода (у нас это от батарейки до другого входа в батарейку). Чтобы из каждой новой точки в плате не идти в обратную сторону, сделаем переменные, в которые будем записывать нашу предыдущую дислокацию. И ещё потребуется переменная, в которую будем запоминать все сопротивления, которые встретились. Для подстраховки сделаем переменную, которая будет отвечать за количество проделанных итераций на случай, если цепь окажется не замкнутой или в ней есть циклы.

Немного забежим вперед и ограничим перемещения по резисторам: если резистор расположен горизонтально (1-3), то тогда можно перемещаться только по вертикали, а если горизонтально (4-6 и об этом в следующей главе), то перемещения доступны только по горизонтали.

Практическая реализация
xr = 2;yr = 1;
xn = 1;yn = 1;
r = 0;
step = 0; tick = 0;

while( ( !((xn==2)&&(yn==1)) )&&(step<500) )
{
step++;
p = PlayerPrefs.GetInt (xn.ToString()+«x»+yn.ToString());
if(p==1) r=r+200;
if(p==2) r=r+500;
if(p==3) r=r+1000;

PlayerPrefs.SetInt(xn.ToString()+«x»+yn.ToString()+«R»,r);

if(( ((xn+1)<=3) && (PlayerPrefs.GetInt ((xn+1).ToString()+«x»+yn.ToString())!=-1) )&&((xn+1)!=xr)&&((p==0)||(p>=4)) )
{
tick++;
xr=xn;
yr=yn;
xn=xn+1;
}
else
if(( ((xn-1)>=1) && (PlayerPrefs.GetInt ((xn-1).ToString()+«x»+yn.ToString())!=-1) )&&((xn-1)!=xr)&&((p==0)||(p>=4)) )
{
tick++;
xr=xn;
yr=yn;
xn=xn-1;
}
else
if(( ((yn+1)<=5) && (PlayerPrefs.GetInt (xn.ToString()+«x»+(yn+1).ToString())!=-1) )&&((yn+1)!=yr)&&(p<=4) )
{
tick++;
yr=yn;
xr=xn;
yn=yn+1;
}
else
if(( ((yn-1)>=1) && (PlayerPrefs.GetInt (xn.ToString()+«x»+(yn-1).ToString())!=-1) )&&((yn-1)!=yr)&&(p<=4))
{
tick++;
yr=yn;
xr=xn;
yn=yn-1;
}
}

Полученное сопротивление обработаем и сохраним в PlayerPrefs. Если сопротивление равно нулю, то заранее установим ток, равным 1 амперу. Если было совершено более 500 шагов, то в схеме есть цикл или же цепь не замкнута.

Обработка и расчет
if(r==0) ir=1.0f;
PlayerPrefs.SetInt («SxemeR», r);
if((step<500)&&(r!=0)) ir=9.0f/r; else
if(step>=500)
{
PlayerPrefs.SetInt («SxemeR», -1);
ir=0.001f;
}

Чтобы получить силу тока, разделим напряжение в батарейке (9В) на сопротивление в цепи. И запишем результат в каждый элемент цепи (для этого пройдем ещё раз по цепи по алгоритму, описанному выше).

?В заключении первой части


— Описанные приемы позволяют создать систему, в которой пользователь сам планирует и размещает элементы цепи;
— Рассмотренный способ представляется интуитивно понятным, но не покрывает все потребности в расчетах;
— В следующей главе будет описано как обойти цепь, используя стек, чтобы не только упростить код, но и разрешить проблему с циклами и разветвлениями (параллельными соединениями в цепи);

P.S. Модели, используемые в проекте можно скачать тут.
Поделиться с друзьями
-->

Комментарии (22)


  1. Virviil
    13.10.2016 16:08
    +1

    Я конечно не знаю, что у вас будет в следующей статье, но на самом деле цепи считаются не так.


    Рассмотрим простейший вариант — статическая цепь, то есть цепь в которой не надо считать переходные процессы.


    Для её расчёта используется метод узловых потенциалов(чаще) или метод контурных токов(реже).


    Преимущества: любая конфигурация сети, любые элементы (конденсаторы и индуктивности), переменный ток работает так же как и постоянные без оверхеда. Если интересно — пишите в личку.


    1. Gr13
      13.10.2016 16:30

      Пока не задумывался про переходные процессы, так как ориентировался только на вычисление сопротивления цепи.
      Дальше собираюсь описать как при помощи стека можно выделить циклы и параллельные соединения (в статических цепях).

      Метод узловых потенциалов решит проблему с мостом, но на данный момент не очень понимаю, как такое реализовать, потому и опустил этот метод


      1. nckma
        14.10.2016 11:50

        У Вас проблемы начнутся, когда Вы захотите рассчитывать цепь содержащую не только резисторы, но и индуктивности с емкостями. Например, сопротивление индуктивности комплексное: jwL и зависит от частоты. Сопротивление конденсатора 1/jwC так же зависит от частоты. Вот методы узловых потенциалов или контурных токов позволяют посчитать и переходные процессы и частотные характеристики цепей.


        1. Gr13
          14.10.2016 12:11

          Осознаю такую сложность, но для этого проекта (в дальнейшем развитии) отказался от переменного тока, равно как и от конденсаторов и катушек индуктивностей.

          возможно рассмотрю их, но это будет крайне не скоро


          1. nckma
            14.10.2016 12:20

            Я допускаю, что видимо Ваша задача состоит в изучении языка C/C++, а не изучении теории цепей. Читателям статьи это не понятно, отсюда и критика.


            1. Gr13
              14.10.2016 12:25

              Цель действительно не в изучении цепей (иначе хаб был не разработка в физика или подобное), а в том, как такое сделать на интуитивно уровне (глава 1) и на уровне программиста (глава 2)


  1. daiver19
    13.10.2016 16:14
    +2

    И какой же стандартный алгоритм вы в итоге реализовали?

    Такого ужасного я, кстати, уже давно не видел.


    1. Gr13
      13.10.2016 16:31

      Обход вершин в графе (ужасным способом, не скрою).

      О том, как это сделать правильно (стек и прочие радости) будет в следующей статье.


      1. daiver19
        13.10.2016 16:37
        +1

        Ну так стандартные алгоритмы — это обход в ширину/глубину. Я уже молчу, что они тривиальны, но у вас их еще и нет в статье про применение стандартных алгоритмов :)


  1. Anton_Menshov
    14.10.2016 01:29
    +1

    Я практически уверен, что вы изобретаете тот еще велосипед. Перед реализацией такой задачи, очень советую прочитать матчасть: метод узловых потенциалов (уже упомянут в комментариях выше).
    Если решать задачу по-настоящему, то стоит использовать модифицированный алгоритм:
    Modified Nodal analysis

    Основная статья по этому методу: Ho, Ruehli, and Brennan, «The modified nodal approach to network analysis».


    1. Gr13
      14.10.2016 03:20

      Спасибо!


    1. lohmatiyy
      14.10.2016 12:24

      А по-хорошему вообще надо было начать с того, как работает анализ по постоянному току в симуляторах типа SPICE.


      1. Gr13
        14.10.2016 12:26

        Как уже ответил выше, цель не изучение цепей, а как можно такое запрограммировать


  1. bambruysk
    14.10.2016 03:20

    А в целом какая цель? Написать на Unity3D свой собственный симулятор цепей?


    1. Gr13
      14.10.2016 03:20

      Очень верно подмечено


      1. armature_current
        14.10.2016 05:03

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


        1. Gr13
          14.10.2016 12:20

          Боюсь, что из за отказа от переменного тока все эти прекрасные технические вещи не реализовать должным образом.

          Потому их добавление зависит от добавления переменного тока в проект


  1. maisvendoo
    14.10.2016 07:08

    Меня смутила (и возмутила!) первая же фраза статьи:

    За время обучения не раз задавался вопросом: все эти хорошие и крутые алгоритмы чем могут помочь на практике?

    То есть как это? Чем же это может помочь программисту знание алгоритмов? Сложный вопрос, дайте ка подумаю…


    1. Gr13
      14.10.2016 12:13

      Не так часто на практике сталкивался с алгоритмами обхода и вот таковой попался в деле

      Возможно достаточно броский, потому так и зацепил


      1. b_oberon
        14.10.2016 13:29

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

        А упомянутые крутые алгоритмы-то где? То, что написано выше, совсем не круто.


      1. yarric
        15.10.2016 09:51

        В частности для обхода графа есть, к примеру, алгоритм Дейкстры.


  1. QtRoS
    14.10.2016 19:32

    Это все же не уровень хабра. И код, и само отношение к алгоритмам как к чему-то далекому и малоприменимому на практике. Тут большинство все же «нюхали порох», и об алгоритмах знают не по наслышке, да и код пишут продакшн качества, а не, пардон, фарш из статьи :)
    Не обижайтесь, это дружеский совет.