Стандартні алгоритми на практиці. Розрахунок ланцюгів. Частина 1

За час навчання не раз задавався питанням: усі ці гарні та круті алгоритми чим можуть допомогти на практиці? І ось, кілька днів тому зіткнувся із завданням з розрахунку ланцюга електричного струму. Те, як це вирішилося і чим — під катом.



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

▍Постановка завдання
Завдання читати з файлу розташування елементів електричної ланцюга, вважати опір всього ланцюга і для кожної ділянки ланцюга встановити силу струму, що протікає через нього. Для спрощення приймемо, що елементи розташовуються на плату 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.0 f;
PlayerPrefs.SetInt («SxemeR», r);
if((step<500)&&(r!=0)) ir=9.0 f/r; else
if(step>=500)
{
PlayerPrefs.SetInt («SxemeR», -1);
ir=0.001 f;
}

Щоб отримати силу струму, розділимо напруга в батарейці (9В) на опір в ланцюзі. І запишемо результат у кожен елемент ланцюга (для цього пройдемо ще раз по ланцюгу за алгоритмом, описаним вище).

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

p.s. Моделі, використовувані в проекті можна скачати тут.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.