Na wypadek, gdyby ktoś był zainteresowany czemu nie ma tutaj nowych wpisów, chciałbym poinformować, że są, tylko w innym miejscu:
Zapraszam!
Na wypadek, gdyby ktoś był zainteresowany czemu nie ma tutaj nowych wpisów, chciałbym poinformować, że są, tylko w innym miejscu:
Zapraszam!
Zanim podzielę się zdjęciami parę zdań wstępu skąd się wziąłem na zachodnim wybrzeżu USA. Przed świętami wielkanocnymi brałem udział w ogromnej (>8k uczestników) konferencji nVidia GTC, która jak co roku odbywała się w centrum konferencyjnym w San Jose w Kalifornii. Sama konferencja to 4 intensywne dni, mnóstwo prezentacji, posterów i wydarzeń: od poniedziałku do czwartku. Konferencję miałem na oku od grudnia, ale finalnie zdecydowałem się jechać jakieś 2-3 tygodnie przed rozpoczęciem – trochę późno na szukanie okazyjnych przelotów i hoteli. Już na wstępnie poszukiwań optymalnego połączenia do Kalifornii okazało się, że najtaniej będzie polecieć w piątek i wrócić w następny piątek. Polecieć można było na 3 sposoby: bezpośrednio LOT-em do Los Angeles, do San Francisco przez Frankfurt albo do San Jose przez Londyn (San Jose to serce doliny krzemowej i leży 80 km od San Francisco). San Jose jak na dolinę krzemową przystało było dramatycznie drogie jeśli chodzi o hotele, więc musiałem “zamieszkać” gdzieś dalej. Szukając różnych opcji finalnie zdecydowałem się na hotel w centrum San Francisco i 4 dni dojazdów so San Jose (bezpośredni pociąg za $10.50). Skoro hotel w San Francisco to i lot do San Francisco. Jak do SF to tylko Lufthansa i przez Frankfurt (na prawdę rewelacyjne połączenie, testowane już 3 raz).
Pozostało pytanie jak zaplanować weekend przed konferencją. Początkowo myślałem o zwiedzeniu Los Angeles przejazd wypożyczonym autem do SF, ale skoro od razu miałem trafić do SF porzuciłem ten pomysł. Potem do głowy wpadł mi inny. Skoro mój samolot ląduje po południu w SF, a konferencja zaczyna się w poniedziałek rano, mam do dyspozycji dwa pełne dni. W SF byłem wcześniej dwa razy, więc chciałem zobaczyć coś innego. Zacząłem się zastanawiać, gdzie można się dostać samolotem z lotniska w SF. Możliwości było dużo, ale najbardziej atrakcyjny i szalony wydał mi się wypad na weekend na Hawaje. Skoro Hawaje i na krótko, to do największego miasta: Honolulu.
Sam wypad na weekend na Hawaje zdecydowanie zasługuje na oddzielny opis, który obiecuję napisać. Dziś chciałem się z Wami podzielić kilkoma ujęciami z Honolulu i SF. Do tej pory zdjęcia wrzucałem na instagram, ale ostatnio doszedłem do wniosku, że forma kwadratu jest okropnym ograniczeniem, więc kilka najlepszych ujęć wrzucam tutaj.
Moje ulubione zdjęcie z Honolulu zrobiłem drugiego (ostatniego) dnia rano ze szczytu krateru Diamond Head:
W San Francisco zrobiłem trzy wieczorne wypady na zdjęcia. Dwa z nich mogę uznać za bardzo udane: do punktu widokowego Battery Spencer przy moście Golden Gate i na wzgórze Twin Peaks. Kika najlepszych ujęć:
Widok na wejście do SF Bay, most Golden Gate i San Francisco:
Most Golden Gate po zachodzie słońca:
Widok na San Francisco i zatokę ze wzgórza Twin Peaks:
Widok na San Francisco ze wzgórza Twin Peaks po zachodzie słońca:
Jak już wcześniej wspominałem w Legionowie pod Warszawą działa 7 zbudowanych przeze mnie urządzeń do pomiaru ilości pyłu zawieszonego w powietrzu. Wszystkie pomiary zbiera i zgrabnie prezentuje strona internetowa zatruci.pl. Od samego początku działania strony słyszałem oficjalne głosy lokalnych władz o niskiej jakości pomiarów, rażących błędach, zawyżaniu wyników itp. Zresztą to trzeba zobaczyć:
Z czasem sprawa trochę ucichła, ale tylko na chwilę. W lutym okazało się, że miasto zakupiło dwa czujniki od Airly. Airly jest liderem polskiego rynku (tak mi się przynajmniej wydaje) “tanich” czujników jakości powietrza. Z tym “tanich” to ostrożnie, bo za czujnik liczą 1500 zł netto + abonamet 50 zł / miesiąc. A co w zamian? Urządzenie mierzy stężenie pyłu zawieszonego, temperaturę, ciśnienie i wilgotność i przesyła dane na serwer. Do transmisji wykorzystuje łączność komórkową – stąd miesięczny abonament. Może więc za tą cenę dostajemy jakiś bardziej wypasiony laserowy sensor pyłu zawieszonego? Nie znalazłem nigdzie informacji jakiego sensora używa Airly, ale wydaje mi się, że widać to na ich filmie promocyjnym (szczególnie od 28 sekundy filmu widać moduł sensora na dwóch ujęciach):
Moim zdaniem jest to czujnik PMS5003. Jest bardzo podobny do tego, którego wykorzystuję w swoich urządzeniach (SEN0177). Na czym polega przewaga Airly? Na tym, że ich zdaniem ich czujniki są skalibrowane aby zapewniać zgodność z profesjonalnymi urządzeniami wykorzystywanymi przez GIOŚ. Wielokrotnie słyszałem zarzut, że moje czujniki nie biorą pod uwagę wilgotności powietrza i zliczają kropelki skondensowanej wody jako pył. Moim zdaniem, jeśli wilgotność miałaby mieć wpływ na pomiar to przez pęcznienie wilgotnych cząstek, a nie jakieś mityczne kropelki. Więc jak to jest z Airly? Sami piszą na swojej stronie trzy ciekawe rzeczy:
W skrócie: wilgotność nie jest brana pod uwagę, a dane są “normowane” na podstawie porównania z czujnikami WIOŚ. Jak rozumiem porównanie zostało wykonane dla jakieś grupy sensorów, a dla reszty jest wykorzysytne dzięki założeniu, że wszystkie sensory tego producenta działają tak samo. Nic o tej normalizacji nie wiemy. Nie wiemy również jak liczone są wyniki prezentowane na ich stronie – z jakiego czasu są uśredniane, w jaki sposób itp.
Miasto wykorzystało jeden czujnik Airly do weryfikacji moich czujników – ustawiali czujnik na kilkanaście godzin obok moich czujników w różnych częściach miasta. Pomysł fajny, niestety Airly udostępniło dane z tych pomiarów z wartościami co 60 minut – bardzo rzadko! Mimo tej niedogodności krótkie spojrzenie na dane pokazuje, że moje czujniki pokazują wyższe stężenia.
Równolegle do tych testów jedna z osób, która kupiła ode mnie czujnik kupuje prywatny czujnik Airly i instaluje go bezpośrednio przy tym ode mnie. Ze swoich czujników mam każdy indywidualny pomiar co kilkaście sekund. Z Airly mam ich przeliczony / poprawiony / normalizowany wynik zapisany co 15 minut. Porównajmy zapisy z kilku ostatnich dni, kiedy czujniki stoją koło siebie.
Porównanie 1: Pomiar Airly vs. indywidualne pomiary z mojego czujnika:
Porównanie 2: Pomiar Airly vs. uśrednione pomiary z mojego czujnika (średnia z poprzedzających 45 minut):
Ponieważ kształt obydwu krzywych jest bardzo podobny zacząłem się zastanawiać jaki najprostszym sposobem uda mi się uzgodnić obie krzywe. Po kilku iteracjach doszedłem do zadowalającego wyniku. Pomnożyłem moje pomiary (już po uśrednieniu) przez współczynnik, którego wartość zależy od stężenia pyłu: dla stężeń od 0 do 20 µg/m3 współczynnik jest równy 1. Dla stężeń od 20 do 90 µg/m3 maleje liniowo od 1 do 0.6. Dla stężeń od 90 do 1000 µg/m3 maleje liniowo od 0.6 do 0.5. Oto wynik porównania po przeliczeniu:
Wychodzi pięknie, ale jaki z tego mamy wniosek? Tu mam problem. Skoro Airly do uzgodnienia pomiarów ze stacjami WIOŚ nie używa temperatury, wilgotności i ciśnienia, to zostaje im współczynnik zależny od samego stężenia. Analogiczny jak opisany powyżej. Pytanie czy takie działanie jest legalne. Dlaczego małe laserowe czujniki miałby tak systematycznie zawyżać pomiary? Który pomiar jest prawdziwy i wiarygodny? Pytanie zostawiam na chwile otwarte, ale obiecuję do tego wrócić!
Obiecałem podzielić się z Wami dokładną instrukcją budowy własnego czujnika smogu. Chciałbym skierować tą instrukcję do osób, które nie zajmują się zawodowo elektroniką i IT, ale jakieś śladowe pojęcie o powyższych posiadają. Są trzy etapy budowy czujnika: pozyskanie podzespołów, zbudowanie czujnika, zaprogramowanie. Takie też będą trzy części tego artykułu.
Cały projekt może być zmodyfikowany na wiele sposobów. Tutaj będę chciał Wam pokazać rozwiązanie moim zdaniem optymalne pod względem kosztów, jakości i wytrzymałości konstrukcji.
Razem wychodzi 622.74 zł brutto (z dodatkowych czujników można zrezygnować – wtedy wyjdzie ok. 500 zł).
W momencie pisania tego wpisu mam oczekujące zamówienia na 3 czujniki, więc zamawiam podzespoły i przy montażu zrobię dla Was instrukcję – zapraszam za ok. tydzień.
Moi mili czytelnicy,
Półtora roku temu napisałem “(…) najnowsze postanowienie o powrocie do pisania na tym blogu (…)”. Przez pół roku udało mi się dotrzymywać postanowienia i publikować fajny content. Niestety potem brak czasu spowodował, że pominąłem kilka tygodnia, aż doprowadziłem do ponad rocznej przerwy. Mea culpa, mea maxima culpa!. Może była to kwestia wyczerpania ciekawych tematów – dalej trzymam się tego, że nie chcę Was tutaj zanudzać głupotami. Ma być ciekawie i na temat. Po co ta spowiedź? Wracamy do pomysłu minimum jednego wpisu na tydzień.
Zapraszam za kilka dni. Przez najbliższy czas będziemy tematycznie oscylować wokół smogu – temat z powodu sezonu jest bardzo gorący.
Przez ostatnie dni media zdominował temat tragicznej jakości powietrza w polskich miastach. Nie jest to problem wirtualny, bo wystarczy wyjść na dwór by zobaczyć i poczuć marną jakość powietrza. Do wytworzenia smogu potrzebujemy dwóch czynników. Pierwszy to produkcja szkodliwych pyłów i zanieczyszczeń, która zimą jest mocno nasilona ze względu na sezon grzewczy, szczególnie w indywidualnych instalacjach C.O. z piecem na paliwo stało (drewno, węgiel), często opalanym odpadami. Elektrociepłownie, mimo, że opalane węglem wydalają proporcjonalnie mniej zanieczyszczeń na kW energii ze względu na zastosowane w instalacji filtry. Drugim, niezbędnym do powstania smogu warunkiem jest wystąpienie warstwy inwersyjnej w atmosferze, która blokuje wymianę powierza przy ziemi, z tym powyżej granicy inwersji. Jeżeli wysokość warstwy inwersyjnej wynosi 100-200 metrów, to znajdujemy się w zamkniętym “słoiku”, do którego wydalamy ogromne ilości zanieczyszczeń. Tyle skróconej teorii, przejdźmy do praktyki.
Wykorzystując bardzo duże stężenie pyłów w Warszawie postanowiłem sprawdzić jak problem wygląda z góry. Udało mi się wykonać dwa bardzo wymowne ujęcia:
Zdjęcie po lewej przedstawia kominy elektrociepłowni Siekierki i warszawskie drapacze chmur w tle. Zdjęcie po prawej to widok na centrum Warszawy z okolicy mostu Gdańskiego. Niestety zdjęcia nie pokazują chmur a właśnie zanieczyszczenia, którymi byliśmy zmuszeni oddychać.
Równolegle uruchomiłem pomiar stężenia pyłów w powietrzu na zewnątrz domu w Legionowie. Zakupiłem laserowy czujnik i połączyłem go z RaspberryPi. Czujnik wisi sobie bezpośrednio za oknem i komunikuje się z jeżyną przez interfejs szeregowy:
Każdy pomiar daje trzy wyniki odpowiadające pyłom o różnych frakcjach, w tym “najpopularniejszy” PM2.5. Program odbierający pomiary napisany w Pythonie:
import serial
from time import gmtime, strftime
port = serial.Serial("/dev/serial0", baudrate=9600, timeout=1.5)
data = port.read(32);
f = open("/home/pi/" + strftime("%Y-%m-%d", gmtime()) + ".txt", "a")
if ord(data[0]) == 66 and ord(data[1])==77:
suma = 0
for a in range(30):
suma += ord(data[a])
if suma == ord(data[30])*256+ord(data[31]):
PM01 = str(ord(data[4])*256+ord(data[5]))
PM25 = str(ord(data[6])*256+ord(data[7]))
PM10 = str(ord(data[8])*256+ord(data[9]))
str = strftime("%Y-%m-%d %H:%M:%S", gmtime()) + "\t" + PM01 + "\t" + PM25 + "\t" + PM10;
f.write(str + "\n")
print(str)
port.close()
f.close()
Powyższy kod uruchamiany jest co minutę. Dane zapisywane są w zwykłym pliku tekstowym:
2017-01-09 20:43:02 185 470 729
2017-01-09 20:44:02 181 440 682
2017-01-09 20:45:02 175 460 701
2017-01-09 20:46:02 172 445 725
2017-01-09 20:47:02 175 448 680
2017-01-09 20:48:02 177 449 725
2017-01-09 20:49:02 170 450 754
2017-01-09 20:50:02 176 458 746
2017-01-09 20:51:02 165 441 733
2017-01-09 20:52:02 174 434 707
2017-01-09 20:53:02 170 451 715
2017-01-09 20:54:02 170 436 702
Następnie raz na 15 minut uruchamiany jest kod rysujący wykres. Dla poprawy czytelności rysowane są średnie kroczące z pomiarów, a nie same pomiary:
import numpy
import ftplib
from ftplib import FTP
import sys
from matplotlib.dates import strpdate2num
from datetime import datetime, timedelta
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
date = sys.argv[1];
X = numpy.loadtxt('/root/AIR/OLSZANKOWA50A/'+date+'.txt', delimiter='\t', dtype=object, converters={0: lambda x: datetime.strptime(x.decode("utf-8"), "%Y-%m-%d %H:%M:%S"), 1: numpy.float, 2: numpy.float, 3: numpy.float})
window = 900;
step = 60;
ts = min(X[:,0])
tss = datetime(ts.year, ts.month, ts.day)
t = tss;
te = t + timedelta(days=1)
T = []
PM01 = []
PM25 = []
PM10 = []
while t < te:
t = t + timedelta(seconds=step)
idx = numpy.logical_and(X[:,0] > (t - timedelta(seconds=window)), X[:,0] < (t + timedelta(seconds=window)))
#print(t, sum(idx))
T.append(t)
if sum(idx) > 0:
PM01.append(numpy.mean(X[idx,1]))
PM25.append(numpy.mean(X[idx,2]))
PM10.append(numpy.mean(X[idx,3]))
else:
PM01.append(numpy.nan)
PM25.append(numpy.nan)
PM10.append(numpy.nan)
x = []
label = []
for a in range(0,24,1):
x.append(tss+timedelta(hours=a))
if a%2 ==0:
label.append((tss+timedelta(hours=a)).strftime('%H:%M:%S'))
else:
label.append('')
plt.figure(figsize=(20, 10))
plt.plot(T,PM01, 'b', lw=2, label='PM 1.00')
plt.plot(T,PM25, 'r', lw=4, label='PM 2.50')
plt.plot(T,PM10, 'g', lw=2, label='PM 10.0')
plt.xlabel('Time [UTC]')
plt.ylabel('ug/m3')
plt.legend(loc='upper left', shadow=True)
plt.xlim([ts, te])
plt.ylim([0, (numpy.ceil(max(PM10)/50)+1)*50])
plt.grid()
plt.title(date)
plt.xticks(x,label, rotation=0)
plt.savefig('/root/AIR/OLSZANKOWA50A/'+date+'.png',dpi=300)
File2Send = '/root/AIR/OLSZANKOWA50A/'+date+'.png'
Output_Directory = "/public_html/parking-legionowo.pl/AIR/"
ftp = FTP('***************')
ftp.login('***************', '***************')
file = open(File2Send, "rb")
ftp.cwd(Output_Directory)
ftp.storbinary('STOR ' +date+'.png', file)
print("STORing File now...")
ftp.quit()
file.close()
print("File transfered")
Po przygotowaniu wykresu jest on zapisywany do pliku png i wysyłany na serwer, gdzie można na bieżąco podglądać pomiary.
Tak wygląda normalny dzień “bez smogu” (te chwilowe wzrosty to prawdopodobnie momenty, gdy sąsiedzi dokładają węgla do starego pieca):
A tak wygląda dzień “smogowy” (akurat wtedy robiłem zdjęcia pokazane na początku):
Aktualne pomiary można zobaczyć na stronie sponsora: http://parking-legionowo.pl/AIR/
Żeby zrobić ładnie zdjęcie dronem trzeba trafić na dobrą pogodę (nie może padać, zbyt mocno wiać, itp.) i dobrą porę dnia (najlepiej okolice wschodu i zachodu słońca, albo ładnie chmury). Są takie wydarzenia, które zdarzają się jeszcze rzadziej, a warte są uwiecznienia za pomocą drona: np. sylwester. Tylko raz w roku jest szansa sfotografowania i nagrania zmasowanego ataku fajerwerkowego. W tym roku w godzinę 0 panowała idealna do lotów pogoda: nie padało, nie wiało, a widoczność była rewelacyjna. Udało mi się wykonać kilka ładnych zdjęć i nagrać film:
Często pytacie mnie o fotografowanie nocą i długie ekspozycje za pomocą drona, więc postaram się niedługo odpowiedzieć Wam przez film na YouTube. Na razie zapraszam na film z nowego roku:
Jeżeli podobają się Wam moje zdjęcia i filmy to obserwujcie mnie na Instagramie (https://www.instagram.com/m.polkowski/) i subskrybujcie na YouTube (https://www.youtube.com/channel/UC0rcoI-FVLZjhIgTgfQpl9Q). Zapraszam!
Co roku z okazji zbliżających się świąt proponuję studentom wstępu do programowania w pythonie na 1 roku w ramach kartkówki zadanie odmóżdżające polegające na przygotowaniu wykresu o tematyce świątecznej. Święta zbiegają się mniej więcej z wprowadzeniem do biblioteki matplotlib, więc i zadanie nie jest mocno naciągane od strony merytorycznej. Gdy dawałem to zadnie pierwszy raz byłem przekonany, że jest to z mojej strony prezent w postaci darmowego punktu do zaliczenia. Niestety co roku zdarzają się osoby, dla których wymyślenie współrzędnych kolejnych wierzchołków łamanej w kształcie choinki jest zbyt trudne.
Zadanie to ma jeszcze jeden ukryty cel – bardzo ładnie pokazuje czy dany student jest dobrym materiałem na fizyka-programistę:
Najważniejsze w tym zadaniu jest nie sugerowania ani efektu końcowego, ani sposobu rozwiązania. Dzięki temu uruchamiana jest kreatywność (lub jej całkowity brak) i podświadome umiejętności programistyczne: czy dana osoba traktuje komputer jak ślepego wykonawce poleceń, czy jako maszynę która potrafi postępować na podstawie algorytmu.
Myślę, że mogłoby to być bardzo sprytne zadanie rekrutacyjne. Moi studenci dostawali na rozwiązanie 15-20 minut. Oto kilka przykładów:
Analizując przepisy odnośnie ograniczeń lotów bezzałogowców w obrębie lotnisk zacząłem się zastanawiać na jakich faktycznie wysokościach latają samoloty rejsowe podczas startów i lądowań.
Wybrałem sobie jeden dzień (zeszły poniedziałek) i zebrałem informacje o wszystkich lotach nad Polską z tego dnia. Plik z danymi z całego dnia zajmuje jakieś 370 mb i wygląda mniej więcej tak:
bbc8f8f,3C5432,55.0600,11.8611,356,34000,354,5412,F-EKEB2,A306,D-AEAR,1480291202,LEJ,OSL,QY3314,0,0,BCS3314,0 bbc663a,394A0B,54.5979,11.8800,52,32000,401,0652,F-EKKL1,B77W,F-GSQL,1480291202,CDG,HND,AF274,0,0,AFR274,0 bbc879c,738076,49.0065,11.9041,120,37000,512,4753,F-ETHF1,B772,4X-ECF,1480291203,LHR,TLV,LY318,0,0,ELY318,0 bbc95e8,40066A,53.9157,12.4301,5,24000,349,6450,F-EDDT2,B752,G-BMRH,1480291202,LEJ,CPH,QY178,0,0,BCS178,0 bbbe339,,50.2147,12.6663,292,40025,385,3253,F-LKPD1,CL60,,1480291203,,,,0,0,CL60,0 bbc715e,406F01,49.8304,12.6811,16,35975,413,1000,F-LKVO1,A320,G-EZOZ,1480291203,BCN,SXF,U24536,0,64,EZY81TC,0 bbc86f4,424293,48.7195,13.1555,114,38975,516,2223,F-LKVO1,B788,VP-BBR,1480291203,LHR,GYD,J28,0,-64,AHY8,0 bbc858c,894070,50.5559,13.2681,112,35000,491,2246,F-LKVO1,A320,A9C-AO,1480291203,LHR,BAH,GF6,0,-64,GFA6,0
Mamy kod lotu, współrzędne geograficzne, wysokość, czas, lotnisko z którego wystartował, lotnisko docelowe, model samolotu itp. Komplet danych załadowałem do Matlaba. W pierwszej kolejności wyrysowałem sobie wszystkie dane:
Sam ogrom informacji na mapie robi wrażenie, wyraźnie widać duży ruch w okolicach Okęcia. Mieliśmy rozmawiać o wysokościach, więc zaznaczam na mapie pozycje kolorami w zależności od wysokości lotu. Zielone to wysokości od 3 do 2 km, żółte od 2 do 1 km, pomarańczowe od 1 km do 500 m i czerwone poniżej 500 m:
Widać, że obszar wokół lotnisk gdzie samoloty latają nisko jest stosunkowo duży. Dla każdego lotniska należałoby przeprowadzić oddzielną analizę. Ja zainteresowałem się Warszawskim Okęciem. Na wykresie naniosłem wysokość lotu (nad poziom terenu) w zależności od odległości w km od lotniska (dla Okęcia liczyłem odległość od przecięcia pasów startowych). Zbiór danych jest bardzo duży, a ich dokładność różna, ale wyraźnie widać różnicą pomiędzy profilem starów (czerwone) i lądowań (niebieskie):
Wyraźnie widać, że w zależności od długości lotu samoloty osiągają różne wysokości przelotowe.
I to co najbardziej interesujące – zbliżenie na samo lotnisko:
Wyraźnie widać, że podczas lądowań samoloty znajdują się dłużej i dalej na małej wysokości. Wydaje się to być zgodne ze zdrowym rozsądkiem. Jakby ktoś był zainteresowany mogę zrobić wykres dla innego lotniska. Bardzo ciekawe jest to zagięcie podczas lądowań równo 15 km od lotniska – może jakiś znawca tematu wyjaśni, czemu to tak wygląda. Wydawało by się, że powinno być łagodne rozpoczęcie zniżania a tu wydaje się gwałtowne.
Dla lubiących kod dziś mam fragment Matlaba rysujący wykres pokazany powyżej:
clear;clc;
T = readtable('all2.txt');
%%
airport1 = table2array(T(:,13));
airport2 = table2array(T(:,14));
flight_lon = table2array(T(:,4));
flight_lat = table2array(T(:,3));
flight_elev = table2array(T(:,6));
flight_code = table2array(T(:,1));
flight_elev = flight_elev * 0.3048;
%%
D = distance(52.165802, 20.967240, flight_lat(idx), flight_lon(idx));
D = deg2km(D);
idxs = strcmp(airport1,'WAW');
idxl = strcmp(airport2,'WAW');
Ds = deg2km(distance(52.165802, 20.967240, flight_lat(idxs), flight_lon(idxs)));
Dl = deg2km(distance(52.165802, 20.967240, flight_lat(idxl), flight_lon(idxl)));
clf
hold on
subplot(2,1,1)
plot(Dl,flight_elev(idxl)/1000,'b.')
title('Planes landing @ WAW')
xlabel('Distance [km]')
ylabel('Elevation [km]')
subplot(2,1,2)
plot(Ds,flight_elev(idxs)/1000,'r.')
title('Planes taking off @ WAW')
xlabel('Distance [km]')
ylabel('Elevation [km]')
print(gcf, '-r360', '-dpng', 'wykres1.png');
Każdy pomysł na fajne zadanie z programowania jest bardzo cenny. Dobre zadanie dla studentów powinno być ciekawe i powiązane z jakimś życiowym problemem, tak aby łatwiej było się skupić na samej istocie programowania a nie na samym problemie. Sam chodziłem kiedyś na zajęcia, gdzie prowadzący wymyślał abstrakcyjne problemy, które łatwo było oprogramować, ale bardzo trudno było zrozumieć o co chodzi i po co to wszystko. Wczoraj otworzyłem nowy wielopak zapałek i okazały się to być te z klasyczną łamigłówką z przekładaniem jednej zapałki:
Pomyślałem, że mogło by to być fajne zadanie dla studentów I roku. Ja zazwyczaj uczę Pythona, więc podjąłem próbę napisania programu. W pierwszej wersji zadaniem programu jest po prostu znalezienie rozwiązania (lub rozwiązań) pojedynczego problemu:
plus = dict()
plus[0] = [8]
plus[1] = [7]
plus[2] = []
plus[3] = [9]
plus[4] = []
plus[5] = [9]
plus[6] = [8]
plus[7] = []
plus[8] = []
plus[9] = [8]
minus = dict()
minus[0] = []
minus[1] = []
minus[2] = []
minus[3] = []
minus[4] = []
minus[5] = []
minus[6] = []
minus[7] = [1]
minus[8] = [0, 6, 9]
minus[9] = [3, 5]
swap = dict()
swap[0] = [6, 9]
swap[1] = []
swap[2] = [3]
swap[3] = [2, 5]
swap[4] = []
swap[5] = [3]
swap[6] = [0, 9]
swap[7] = []
swap[8] = []
swap[9] = [0, 6]
def test_single(a, o1, b, o2, c):
if o2 == "=":
if o1 == "+":
return a+b==c
elif o1 == "-":
return a-b==c
else:
print('ERROR')
elif o1 == "=":
if o2 == "+":
return a+b==c
elif o2 == "-":
return a-b==c
else:
print('ERROR')
else:
print('ERROR')
def print_result(a, o1, b, o2, c,debug):
print("{5}: {0}{1}{2}{3}{4}:".format(a,o1,b,o2,c,debug), test_single(a, o1, b, o2, c));
def all_comb(a, o1, b, o2, c):
#print("SWAPY")
for a1 in swap[a]:
print_result(a1, o1, b, o2, c,1)
for b1 in swap[b]:
print_result(a, o1, b1, o2, c,2)
for c1 in swap[c]:
print_result(a, o1, b, o2, c1,3)
if o1 == '-':
print_result(a, "=", b, "-", c,4)
#print("A minus")
for a1 in minus[a]:
for b1 in plus[b]:
print_result(a1, o1, b1, o2, c,5)
for c1 in plus[c]:
print_result(a1, o1, b, o2, c1,6)
if o1 == "-":
print_result(a1, "+", b, o2, c,7)
if o2 == "-":
print_result(a1, o1, b, "+", c,8)
#print("B minus")
for b1 in minus[b]:
for a1 in plus[a]:
print_result(a1, o1, b1, o2, c,9)
for c1 in plus[c]:
print_result(a, o1, b1, o2, c1,10)
if o1 == "-":
print_result(a, "+", b1, o2, c,11)
if o2 == "-":
print_result(a, o1, b1, "+", c,12)
#print("C minus")
for c1 in minus[c]:
for b1 in plus[b]:
print_result(a, o1, b1, o2, c1,13)
for a1 in plus[a]:
print_result(a1, o1, b, o2, c1,14)
if o1 == "-":
print_result(a, "+", b, o2, c1,15)
if o2 == "-":
print_result(a, o1, b, "+", c1,16)
#print("+ minus")
if o1 == "+":
for a1 in plus[a]:
print_result(a1, "-", b, o2, c,17)
for b1 in plus[b]:
print_result(a, "-", b1, o2, c,18)
for c1 in plus[c]:
print_result(a, "-", b, o2, c1,19)
all_comb(1,"+",2,"=",8)
Nie jest to rozwiązanie w żaden sposób zoptymalizowane. Byłbym zadowolony, gdyby na zajęciach udało się dojść do takiego rozwiązania. Dla równania 1+2=8 program zwraca taki wynik:
Działanie programu opiera się po pierwsze na 3 zdefiniowanych słownikach, które określają na jaką cyfrę można zmienić daną cyfrę po odjęciu, dodaniu i przełożeniu jednej zapałki. Po drugie program przewiduje wszystkie przypadki zamian: np. odjęcie zapałki z pierwszej cyfry i dodanie do cyfry drugiej lub trzeciej. W sumie przewidziane jest 19 przypadków (może da się to zoptymalizować?).
Drugą, równie ciekawą wersją programu jest wygenerowanie wszystkich możliwych łamigłówek. Tu należy zdecydować, czy rozważamy te równania, które są poprawne na wejściu. Ja zdecydowałem ich nie pomijać, bo wymagamy poprawności po przełożeniu jednej zapałki. Kod wygląda tak:
plus = dict()
plus[0] = [8]
plus[1] = [7]
plus[2] = []
plus[3] = [9]
plus[4] = []
plus[5] = [9]
plus[6] = [8]
plus[7] = []
plus[8] = []
plus[9] = [8]
minus = dict()
minus[0] = []
minus[1] = []
minus[2] = []
minus[3] = []
minus[4] = []
minus[5] = []
minus[6] = []
minus[7] = [1]
minus[8] = [0, 6, 9]
minus[9] = [3, 5]
swap = dict()
swap[0] = [6, 9]
swap[1] = []
swap[2] = [3]
swap[3] = [2, 5]
swap[4] = []
swap[5] = [3]
swap[6] = [0, 9]
swap[7] = []
swap[8] = []
swap[9] = [0, 6]
def test_single(a, o1, b, o2, c):
if o2 == "=":
if o1 == "+":
return a+b==c
elif o1 == "-":
return a-b==c
else:
print('ERROR')
elif o1 == "=":
if o2 == "+":
return a+b==c
elif o2 == "-":
return a-b==c
else:
print('ERROR')
else:
print('ERROR')
def print_result(wa,wo1,wb,wo2,wc,a, o1, b, o2, c):
if test_single(a, o1, b, o2, c):
print("{0}{1}{2}{3}{4} -> {5}{6}{7}{8}{9}".format(wa,wo1,wb,wo2,wc,a,o1,b,o2,c))
return 1
else:
return 0;
def all_comb(a, o1, b, o2, c):
count = 0
for a1 in swap[a]:
count = count + print_result(a,o1,b,o2,c,a1, o1, b, o2, c)
for b1 in swap[b]:
count = count + print_result(a,o1,b,o2,c,a, o1, b1, o2, c)
for c1 in swap[c]:
count = count + print_result(a,o1,b,o2,c,a, o1, b, o2, c1)
if o1 == '-':
count = count + print_result(a,o1,b,o2,c,a, "=", b, "-", c)
for a1 in minus[a]:
for b1 in plus[b]:
count = count + print_result(a,o1,b,o2,c,a1, o1, b1, o2, c)
for c1 in plus[c]:
count = count + print_result(a,o1,b,o2,c,a1, o1, b, o2, c1)
if o1 == "-":
count = count + print_result(a,o1,b,o2,c,a1, "+", b, o2, c)
if o2 == "-":
count = count + print_result(a,o1,b,o2,c,a1, o1, b, "+", c)
for b1 in minus[b]:
for a1 in plus[a]:
count = count + print_result(a,o1,b,o2,c,a1, o1, b1, o2, c)
for c1 in plus[c]:
count = count + print_result(a,o1,b,o2,c,a, o1, b1, o2, c1)
if o1 == "-":
count = count + print_result(a,o1,b,o2,c,a, "+", b1, o2, c)
if o2 == "-":
count = count + print_result(a,o1,b,o2,c,a, o1, b1, "+", c)
for c1 in minus[c]:
for b1 in plus[b]:
count = count + print_result(a,o1,b,o2,c,a, o1, b1, o2, c1)
for a1 in plus[a]:
count = count + print_result(a,o1,b,o2,c,a1, o1, b, o2, c1)
if o1 == "-":
count = count + print_result(a,o1,b,o2,c,a, "+", b, o2, c1)
if o2 == "-":
count = count + print_result(a,o1,b,o2,c,a, o1, b, "+", c1)
if o1 == "+":
for a1 in plus[a]:
count = count + print_result(a,o1,b,o2,c,a1, "-", b, o2, c)
for b1 in plus[b]:
count = count + print_result(a,o1,b,o2,c,a, "-", b1, o2, c)
for c1 in plus[c]:
count = count + print_result(a,o1,b,o2,c,a, "-", b, o2, c1)
return count
for to1 in ["+", "-"]:
for ta in range(10):
for tb in range(10):
for tc in range(10):
tcount = all_comb(ta,to1,tb,"=",tc)
#if tcount > 2:
# print("{0}{1}{2}{3}{4}".format(ta,to1,tb,"=",tc), tcount)
Wynik jest obliczany w mgnieniu oka. Dla rozwiązania dla łamigłówek z obrazka są następujące:
Lista wszystkich kombinacji dla potomnych:
0+0=6 -> 6+0=6
0+0=6 -> 0+6=6
0+0=6 -> 0+0=0
0+0=8 -> 8-0=8
0+0=9 -> 9+0=9
0+0=9 -> 0+9=9
0+0=9 -> 0+0=0
0+1=7 -> 6+1=7
0+1=7 -> 8-1=7
0+1=8 -> 8+1=9
0+2=3 -> 0+3=3
0+2=3 -> 0+2=2
0+2=6 -> 8-2=6
0+2=8 -> 6+2=8
0+3=2 -> 0+2=2
0+3=2 -> 0+3=3
0+3=5 -> 0+5=5
0+3=5 -> 0+3=3
0+3=5 -> 8-3=5
0+3=8 -> 0+9=9
0+3=9 -> 6+3=9
0+4=4 -> 8-4=4
0+5=3 -> 0+3=3
0+5=3 -> 0+5=5
0+5=3 -> 8-5=3
0+5=8 -> 0+9=9
0+6=0 -> 0+0=0
0+6=0 -> 0+6=6
0+6=2 -> 8-6=2
0+6=9 -> 0+9=9
0+6=9 -> 0+6=6
0+7=1 -> 8-7=1
0+7=9 -> 8+1=9
0+8=0 -> 8-8=0
0+8=3 -> 0+9=9
0+8=5 -> 0+9=9
0+8=8 -> 8+0=8
0+9=0 -> 0+0=0
0+9=0 -> 0+9=9
0+9=6 -> 0+6=6
0+9=6 -> 0+9=9
1+0=7 -> 1+6=7
1+0=7 -> 7-0=7
1+0=8 -> 1+8=9
1+1=3 -> 1+1=2
1+1=6 -> 7-1=6
1+2=2 -> 1+2=3
1+2=4 -> 1+3=4
1+2=5 -> 1+2=3
1+2=5 -> 7-2=5
1+2=8 -> 7+2=9
1+3=3 -> 1+2=3
1+3=4 -> 7-3=4
1+3=6 -> 1+5=6
1+4=3 -> 1+4=5
1+4=3 -> 7-4=3
1+5=0 -> 1+5=6
1+5=2 -> 7-5=2
1+5=4 -> 1+3=4
1+5=9 -> 1+5=6
1+6=1 -> 1+0=1
1+6=1 -> 7-6=1
1+6=8 -> 1+8=9
1+7=0 -> 7-7=0
1+7=8 -> 7+1=8
1+8=0 -> 1+8=9
1+8=1 -> 1+6=7
1+8=6 -> 1+8=9
1+8=7 -> 7+0=7
1+9=1 -> 1+0=1
1+9=7 -> 1+6=7
1+9=8 -> 1+8=9
2+0=3 -> 3+0=3
2+0=3 -> 2+0=2
2+0=8 -> 2+6=8
2+1=2 -> 2+1=3
2+1=4 -> 3+1=4
2+1=5 -> 2+1=3
2+1=8 -> 2+7=9
2+2=5 -> 3+2=5
2+2=5 -> 2+3=5
2+3=3 -> 2+3=5
2+3=4 -> 2+2=4
2+3=6 -> 3+3=6
2+3=7 -> 2+5=7
2+4=0 -> 2+4=6
2+4=7 -> 3+4=7
2+4=9 -> 2+4=6
2+5=5 -> 2+3=5
2+5=8 -> 3+5=8
2+6=2 -> 2+0=2
2+6=9 -> 3+6=9
2+7=0 -> 2+7=9
2+7=6 -> 2+7=9
2+8=0 -> 2+6=8
2+8=6 -> 2+6=8
2+8=9 -> 2+6=8
2+9=1 -> 2+5=7
2+9=2 -> 2+0=2
2+9=8 -> 2+6=8
3+0=2 -> 2+0=2
3+0=2 -> 3+0=3
3+0=5 -> 5+0=5
3+0=5 -> 3+0=3
3+0=8 -> 9+0=9
3+0=9 -> 3+6=9
3+0=9 -> 9-0=9
3+1=3 -> 2+1=3
3+1=6 -> 5+1=6
3+1=8 -> 9-1=8
3+2=3 -> 3+2=5
3+2=4 -> 2+2=4
3+2=6 -> 3+3=6
3+2=7 -> 5+2=7
3+2=7 -> 9-2=7
3+3=0 -> 3+3=6
3+3=5 -> 2+3=5
3+3=5 -> 3+2=5
3+3=6 -> 9-3=6
3+3=8 -> 5+3=8
3+3=8 -> 3+5=8
3+3=9 -> 3+3=6
3+4=5 -> 9-4=5
3+4=6 -> 2+4=6
3+4=9 -> 5+4=9
3+5=4 -> 9-5=4
3+5=6 -> 3+3=6
3+5=7 -> 2+5=7
3+6=0 -> 3+6=9
3+6=3 -> 3+0=3
3+6=3 -> 9-6=3
3+6=6 -> 3+6=9
3+6=8 -> 2+6=8
3+7=2 -> 9-7=2
3+7=9 -> 2+7=9
3+8=1 -> 9-8=1
3+8=3 -> 3+6=9
3+8=5 -> 3+6=9
3+8=9 -> 9+0=9
3+9=0 -> 3+5=8
3+9=0 -> 9-9=0
3+9=3 -> 3+0=3
3+9=6 -> 3+5=8
3+9=9 -> 3+6=9
3+9=9 -> 3+5=8
4+1=3 -> 4+1=5
4+2=0 -> 4+2=6
4+2=7 -> 4+3=7
4+2=9 -> 4+2=6
4+3=6 -> 4+2=6
4+3=9 -> 4+5=9
4+5=0 -> 4+5=9
4+5=6 -> 4+5=9
4+5=7 -> 4+3=7
4+6=4 -> 4+0=4
4+9=1 -> 4+3=7
4+9=3 -> 4+5=9
4+9=4 -> 4+0=4
4+9=5 -> 4+5=9
5+0=3 -> 3+0=3
5+0=3 -> 5+0=5
5+0=8 -> 9+0=9
5+0=9 -> 9-0=9
5+1=0 -> 5+1=6
5+1=4 -> 3+1=4
5+1=8 -> 9-1=8
5+1=9 -> 5+1=6
5+2=5 -> 3+2=5
5+2=7 -> 9-2=7
5+2=8 -> 5+3=8
5+3=6 -> 3+3=6
5+3=6 -> 9-3=6
5+3=7 -> 5+2=7
5+4=0 -> 5+4=9
5+4=5 -> 9-4=5
5+4=6 -> 5+4=9
5+4=7 -> 3+4=7
5+5=4 -> 9-5=4
5+5=8 -> 3+5=8
5+5=8 -> 5+3=8
5+6=3 -> 9-6=3
5+6=5 -> 5+0=5
5+6=9 -> 3+6=9
5+7=2 -> 9-7=2
5+8=1 -> 9-8=1
5+8=9 -> 9+0=9
5+9=0 -> 5+3=8
5+9=0 -> 9-9=0
5+9=5 -> 5+0=5
5+9=6 -> 5+3=8
5+9=9 -> 5+3=8
6+0=0 -> 0+0=0
6+0=0 -> 6+0=6
6+0=8 -> 8-0=8
6+0=9 -> 9+0=9
6+0=9 -> 6+0=6
6+1=1 -> 0+1=1
6+1=7 -> 8-1=7
6+1=8 -> 8+1=9
6+2=2 -> 0+2=2
6+2=6 -> 8-2=6
6+2=9 -> 6+3=9
6+3=0 -> 6+3=9
6+3=3 -> 0+3=3
6+3=5 -> 8-3=5
6+3=6 -> 6+3=9
6+3=8 -> 6+2=8
6+4=4 -> 0+4=4
6+4=4 -> 8-4=4
6+5=3 -> 8-5=3
6+5=5 -> 0+5=5
6+5=9 -> 6+3=9
6+6=2 -> 8-6=2
6+6=6 -> 0+6=6
6+6=6 -> 6+0=6
6+7=1 -> 6+1=7
6+7=1 -> 8-7=1
6+7=7 -> 0+7=7
6+7=9 -> 8+1=9
6+8=0 -> 8-8=0
6+8=8 -> 0+8=8
6+8=8 -> 8+0=8
6+9=3 -> 6+3=9
6+9=5 -> 6+3=9
6+9=6 -> 6+0=6
6+9=9 -> 0+9=9
7+0=1 -> 7-0=7
7+0=9 -> 1+8=9
7+1=0 -> 7-7=0
7+1=8 -> 1+7=8
7+2=0 -> 7+2=9
7+2=6 -> 7+2=9
7+3=9 -> 7+2=9
7+6=1 -> 1+6=7
7+6=7 -> 7+0=7
7+6=9 -> 1+8=9
7+7=0 -> 1+7=8
7+7=0 -> 7+1=8
7+7=6 -> 1+7=8
7+7=6 -> 7+1=8
7+7=9 -> 1+7=8
7+7=9 -> 7+1=8
7+8=1 -> 7+0=7
7+8=3 -> 1+8=9
7+8=5 -> 1+8=9
7+9=7 -> 7+0=7
7+9=9 -> 1+8=9
8+0=0 -> 8-8=0
8+0=0 -> 8-0=8
8+0=3 -> 9+0=9
8+0=5 -> 9+0=9
8+0=6 -> 8-0=8
8+0=8 -> 0+8=8
8+0=9 -> 8-0=8
8+1=0 -> 8+1=9
8+1=1 -> 6+1=7
8+1=1 -> 8-7=1
8+1=1 -> 8-1=7
8+1=6 -> 8+1=9
8+1=7 -> 0+7=7
8+2=0 -> 6+2=8
8+2=6 -> 6+2=8
8+2=9 -> 6+2=8
8+3=3 -> 6+3=9
8+3=5 -> 6+3=9
8+3=9 -> 0+9=9
8+5=9 -> 0+9=9
8+6=0 -> 8-8=0
8+6=8 -> 8+0=8
8+6=8 -> 0+8=8
8+7=1 -> 0+7=7
8+7=3 -> 8+1=9
8+7=5 -> 8+1=9
8+8=0 -> 0+8=8
8+8=0 -> 8+0=8
8+8=6 -> 0+8=8
8+8=6 -> 8+0=8
8+8=9 -> 0+8=8
8+8=9 -> 8+0=8
8+9=0 -> 8-8=0
8+9=3 -> 0+9=9
8+9=5 -> 0+9=9
8+9=8 -> 8+0=8
8+9=8 -> 0+8=8
9+0=0 -> 0+0=0
9+0=0 -> 9+0=9
9+0=1 -> 9-8=1
9+0=3 -> 9-0=9
9+0=5 -> 9-0=9
9+0=6 -> 6+0=6
9+0=6 -> 9+0=9
9+0=8 -> 8-0=8
9+1=0 -> 9-1=8
9+1=1 -> 0+1=1
9+1=2 -> 9-7=2
9+1=6 -> 9-1=8
9+1=7 -> 6+1=7
9+1=7 -> 8-1=7
9+1=8 -> 8+1=9
9+1=9 -> 9-1=8
9+2=1 -> 5+2=7
9+2=1 -> 9-2=7
9+2=2 -> 0+2=2
9+2=6 -> 8-2=6
9+2=8 -> 6+2=8
9+3=0 -> 5+3=8
9+3=0 -> 9-9=0
9+3=3 -> 0+3=3
9+3=5 -> 8-3=5
9+3=6 -> 5+3=8
9+3=9 -> 6+3=9
9+3=9 -> 5+3=8
9+4=1 -> 3+4=7
9+4=3 -> 5+4=9
9+4=4 -> 0+4=4
9+4=4 -> 8-4=4
9+4=5 -> 5+4=9
9+5=0 -> 3+5=8
9+5=0 -> 9-9=0
9+5=3 -> 8-5=3
9+5=5 -> 0+5=5
9+5=6 -> 3+5=8
9+5=9 -> 3+5=8
9+6=1 -> 9-8=1
9+6=2 -> 8-6=2
9+6=3 -> 3+6=9
9+6=5 -> 3+6=9
9+6=6 -> 0+6=6
9+6=9 -> 9+0=9
9+7=1 -> 8-7=1
9+7=7 -> 0+7=7
9+7=9 -> 8+1=9
9+8=0 -> 8-8=0
9+8=3 -> 9+0=9
9+8=5 -> 9+0=9
9+8=8 -> 0+8=8
9+8=8 -> 8+0=8
9+9=1 -> 9-8=1
9+9=9 -> 0+9=9
9+9=9 -> 9+0=9
0-0=0 -> 0=0-0
0-0=6 -> 6-0=6
0-0=6 -> 0-0=0
0-0=8 -> 0+0=0
0-0=9 -> 9-0=9
0-0=9 -> 0-0=0
0-1=5 -> 6-1=5
0-1=7 -> 0+1=1
0-1=8 -> 9-1=8
0-2=4 -> 6-2=4
0-2=7 -> 9-2=7
0-2=8 -> 8-2=6
0-3=3 -> 6-3=3
0-3=6 -> 9-3=6
0-3=9 -> 0+3=3
0-3=9 -> 8-3=5
0-4=2 -> 6-4=2
0-4=5 -> 9-4=5
0-5=1 -> 6-5=1
0-5=4 -> 9-5=4
0-5=9 -> 8-5=3
0-5=9 -> 0+5=5
0-6=0 -> 6-6=0
0-6=0 -> 0-0=0
0-6=3 -> 9-6=3
0-6=8 -> 0+6=6
0-7=1 -> 0+1=1
0-7=2 -> 9-7=2
0-7=7 -> 8-1=7
0-7=7 -> 8-7=1
0-8=0 -> 0+0=0
0-8=1 -> 9-8=1
0-8=2 -> 8-6=2
0-8=6 -> 0+6=6
0-8=8 -> 8-0=8
0-8=8 -> 8-8=0
0-8=9 -> 0+9=9
0-9=0 -> 9-9=0
0-9=0 -> 0-0=0
0-9=3 -> 0+3=3
0-9=3 -> 8-5=3
0-9=5 -> 8-3=5
0-9=5 -> 0+5=5
0-9=8 -> 0+9=9
1-0=1 -> 1=0-1
1-0=7 -> 1+0=1
1-1=0 -> 1=1-0
1-1=6 -> 1-1=0
1-1=8 -> 7-1=6
1-1=9 -> 1-1=0
1-2=9 -> 1+2=3
1-2=9 -> 7-2=5
1-4=9 -> 7-4=3
1-4=9 -> 1+4=5
1-5=8 -> 1+5=6
1-6=1 -> 1-0=1
1-6=7 -> 7-6=1
1-7=2 -> 1+1=2
1-7=6 -> 7-1=6
1-7=8 -> 7-7=0
1-8=1 -> 1+0=1
1-8=1 -> 7-6=1
1-8=7 -> 7-0=7
1-8=7 -> 1+6=7
1-8=8 -> 1+8=9
1-9=1 -> 1-0=1
1-9=2 -> 7-5=2
1-9=4 -> 7-3=4
1-9=4 -> 1+3=4
1-9=6 -> 1+5=6
2-0=2 -> 2=0-2
2-0=3 -> 3-0=3
2-0=3 -> 2-0=2
2-1=1 -> 2=1-1
2-1=2 -> 3-1=2
2-1=9 -> 2+1=3
2-2=0 -> 2=2-0
2-2=1 -> 3-2=1
2-2=6 -> 2-2=0
2-2=9 -> 2-2=0
2-3=0 -> 3-3=0
2-3=0 -> 2-2=0
2-3=9 -> 2+3=5
2-4=8 -> 2+4=6
2-6=2 -> 2-0=2
2-7=3 -> 2+1=3
2-7=8 -> 2+7=9
2-8=2 -> 2+0=2
2-8=8 -> 2+6=8
2-9=2 -> 2-0=2
2-9=5 -> 2+3=5
2-9=7 -> 2+5=7
3-0=2 -> 2-0=2
3-0=2 -> 3-0=3
3-0=3 -> 3=0-3
3-0=5 -> 5-0=5
3-0=5 -> 3-0=3
3-0=8 -> 9-0=9
3-0=9 -> 3+0=3
3-1=1 -> 2-1=1
3-1=2 -> 3=1-2
3-1=3 -> 3-1=2
3-1=4 -> 5-1=4
3-2=0 -> 2-2=0
3-2=0 -> 3-3=0
3-2=1 -> 3=2-1
3-2=3 -> 5-2=3
3-2=9 -> 3+2=5
3-3=0 -> 3=3-0
3-3=1 -> 3-2=1
3-3=2 -> 5-3=2
3-3=6 -> 3-3=0
3-3=8 -> 9-3=6
3-3=8 -> 3+3=6
3-3=9 -> 3-3=0
3-4=1 -> 5-4=1
3-4=9 -> 9-4=5
3-5=0 -> 5-5=0
3-5=0 -> 3-3=0
3-6=3 -> 3-0=3
3-6=8 -> 3+6=9
3-6=9 -> 9-6=3
3-7=4 -> 3+1=4
3-7=8 -> 9-1=8
3-8=0 -> 9-9=0
3-8=3 -> 3+0=3
3-8=3 -> 9-6=3
3-8=7 -> 9-8=1
3-8=9 -> 9-0=9
3-8=9 -> 3+6=9
3-9=3 -> 3-0=3
3-9=4 -> 9-5=4
3-9=6 -> 9-3=6
3-9=6 -> 3+3=6
3-9=8 -> 3+5=8
3-9=8 -> 9-9=0
4-0=4 -> 4=0-4
4-1=2 -> 4-1=3
4-1=3 -> 4=1-3
4-1=5 -> 4-1=3
4-1=9 -> 4+1=5
4-2=1 -> 4-3=1
4-2=2 -> 4=2-2
4-2=3 -> 4-2=2
4-2=8 -> 4+2=6
4-3=1 -> 4=3-1
4-3=2 -> 4-2=2
4-4=0 -> 4=4-0
4-4=6 -> 4-4=0
4-4=9 -> 4-4=0
4-5=1 -> 4-3=1
4-5=8 -> 4+5=9
4-6=4 -> 4-0=4
4-7=5 -> 4+1=5
4-8=4 -> 4+0=4
4-9=4 -> 4-0=4
4-9=7 -> 4+3=7
4-9=9 -> 4+5=9
5-0=3 -> 3-0=3
5-0=3 -> 5-0=5
5-0=5 -> 5=0-5
5-0=8 -> 9-0=9
5-0=9 -> 5+0=5
5-1=2 -> 3-1=2
5-1=4 -> 5=1-4
5-1=8 -> 5+1=6
5-2=1 -> 3-2=1
5-2=2 -> 5-3=2
5-2=2 -> 5-2=3
5-2=3 -> 5=2-3
5-2=5 -> 5-2=3
5-3=0 -> 3-3=0
5-3=0 -> 5-5=0
5-3=2 -> 5=3-2
5-3=3 -> 5-2=3
5-3=3 -> 5-3=2
5-3=8 -> 9-3=6
5-4=1 -> 5=4-1
5-4=8 -> 5+4=9
5-4=9 -> 9-4=5
5-5=0 -> 5=5-0
5-5=2 -> 5-3=2
5-5=6 -> 5-5=0
5-5=9 -> 5-5=0
5-6=5 -> 5-0=5
5-6=9 -> 9-6=3
5-7=6 -> 5+1=6
5-7=8 -> 9-1=8
5-8=0 -> 9-9=0
5-8=3 -> 9-6=3
5-8=5 -> 5+0=5
5-8=7 -> 9-8=1
5-8=9 -> 9-0=9
5-9=4 -> 9-5=4
5-9=5 -> 5-0=5
5-9=6 -> 9-3=6
5-9=8 -> 5+3=8
5-9=8 -> 9-9=0
6-0=0 -> 0-0=0
6-0=0 -> 6-6=0
6-0=0 -> 6-0=6
6-0=6 -> 6=0-6
6-0=8 -> 6+0=6
6-0=9 -> 9-0=9
6-0=9 -> 6-0=6
6-1=3 -> 6-1=5
6-1=5 -> 6=1-5
6-1=8 -> 9-1=8
6-2=3 -> 6-3=3
6-2=4 -> 6=2-4
6-2=7 -> 9-2=7
6-2=8 -> 8-2=6
6-3=1 -> 6-5=1
6-3=2 -> 6-3=3
6-3=3 -> 6=3-3
6-3=4 -> 6-2=4
6-3=5 -> 6-3=3
6-3=6 -> 9-3=6
6-3=8 -> 6+3=9
6-3=9 -> 8-3=5
6-4=2 -> 6=4-2
6-4=3 -> 6-4=2
6-4=5 -> 9-4=5
6-5=1 -> 6=5-1
6-5=3 -> 6-3=3
6-5=4 -> 9-5=4
6-5=9 -> 8-5=3
6-6=0 -> 6=6-0
6-6=3 -> 9-6=3
6-6=6 -> 6-0=6
6-6=6 -> 6-6=0
6-6=9 -> 6-6=0
6-7=2 -> 9-7=2
6-7=7 -> 8-1=7
6-7=7 -> 6+1=7
6-7=7 -> 8-7=1
6-8=1 -> 9-8=1
6-8=2 -> 8-6=2
6-8=6 -> 6+0=6
6-8=8 -> 8-0=8
6-8=8 -> 8-8=0
6-9=0 -> 9-9=0
6-9=0 -> 6-6=0
6-9=3 -> 8-5=3
6-9=5 -> 8-3=5
6-9=6 -> 6-0=6
6-9=9 -> 6+3=9
7-0=1 -> 7-6=1
7-0=1 -> 1+0=1
7-0=7 -> 7=0-7
7-1=0 -> 7-1=6
7-1=2 -> 1+1=2
7-1=6 -> 7=1-6
7-1=8 -> 7-7=0
7-1=9 -> 7-1=6
7-2=3 -> 7-2=5
7-2=3 -> 1+2=3
7-2=4 -> 7-3=4
7-2=5 -> 7=2-5
7-2=8 -> 7+2=9
7-3=2 -> 7-5=2
7-3=4 -> 7=3-4
7-3=4 -> 1+3=4
7-3=5 -> 7-2=5
7-4=2 -> 7-4=3
7-4=3 -> 7=4-3
7-4=5 -> 7-4=3
7-4=5 -> 1+4=5
7-5=2 -> 7=5-2
7-5=3 -> 7-5=2
7-5=4 -> 7-3=4
7-5=6 -> 1+5=6
7-6=1 -> 7=6-1
7-6=7 -> 7-0=7
7-6=7 -> 1+6=7
7-7=0 -> 7=7-0
7-7=6 -> 7-7=0
7-7=8 -> 1+7=8
7-7=8 -> 7+1=8
7-7=9 -> 7-7=0
7-8=1 -> 7-0=7
7-8=7 -> 7+0=7
7-8=9 -> 1+8=9
7-9=1 -> 7-6=1
7-9=7 -> 7-0=7
8-0=0 -> 0+0=0
8-0=1 -> 9-8=1
8-0=2 -> 8-6=2
8-0=3 -> 9-0=9
8-0=5 -> 9-0=9
8-0=6 -> 6+0=6
8-0=8 -> 8=0-8
8-0=8 -> 8-8=0
8-0=9 -> 9+0=9
8-1=0 -> 9-1=8
8-1=1 -> 0+1=1
8-1=2 -> 9-7=2
8-1=6 -> 9-1=8
8-1=7 -> 8=1-7
8-1=7 -> 6+1=7
8-1=7 -> 8-7=1
8-1=8 -> 8+1=9
8-1=9 -> 9-1=8
8-2=0 -> 8-2=6
8-2=1 -> 9-2=7
8-2=2 -> 0+2=2
8-2=5 -> 8-3=5
8-2=6 -> 8=2-6
8-2=8 -> 6+2=8
8-2=9 -> 8-2=6
8-3=0 -> 9-9=0
8-3=3 -> 8-5=3
8-3=3 -> 8-3=5
8-3=3 -> 0+3=3
8-3=5 -> 8=3-5
8-3=6 -> 8-2=6
8-3=9 -> 6+3=9
8-4=4 -> 8=4-4
8-4=4 -> 0+4=4
8-5=0 -> 9-9=0
8-5=2 -> 8-5=3
8-5=3 -> 8=5-3
8-5=5 -> 8-3=5
8-5=5 -> 8-5=3
8-5=5 -> 0+5=5
8-6=1 -> 9-8=1
8-6=2 -> 8=6-2
8-6=3 -> 8-6=2
8-6=6 -> 0+6=6
8-6=8 -> 8-0=8
8-6=8 -> 8-8=0
8-7=1 -> 8=7-1
8-7=1 -> 8-1=7
8-7=7 -> 0+7=7
8-7=9 -> 8+1=9
8-8=0 -> 8=8-0
8-8=0 -> 8-0=8
8-8=6 -> 8-8=0
8-8=6 -> 8-0=8
8-8=8 -> 0+8=8
8-8=8 -> 8+0=8
8-8=9 -> 8-8=0
8-8=9 -> 8-0=8
8-9=1 -> 9-8=1
8-9=2 -> 8-6=2
8-9=8 -> 8-0=8
8-9=8 -> 8-8=0
8-9=9 -> 0+9=9
9-0=0 -> 0-0=0
9-0=0 -> 9-9=0
9-0=0 -> 9-0=9
9-0=3 -> 9-6=3
9-0=3 -> 3+0=3
9-0=5 -> 5+0=5
9-0=6 -> 6-0=6
9-0=6 -> 9-0=9
9-0=7 -> 9-8=1
9-0=8 -> 9+0=9
9-0=9 -> 9=0-9
9-1=4 -> 3+1=4
9-1=5 -> 6-1=5
9-1=6 -> 5+1=6
9-1=8 -> 9=1-8
9-2=4 -> 6-2=4
9-2=5 -> 3+2=5
9-2=6 -> 9-3=6
9-2=7 -> 9=2-7
9-2=7 -> 5+2=7
9-2=8 -> 8-2=6
9-3=0 -> 9-3=6
9-3=3 -> 6-3=3
9-3=4 -> 9-5=4
9-3=6 -> 9=3-6
9-3=6 -> 3+3=6
9-3=7 -> 9-2=7
9-3=8 -> 5+3=8
9-3=8 -> 9-9=0
9-3=9 -> 9-3=6
9-3=9 -> 8-3=5
9-4=2 -> 6-4=2
9-4=3 -> 9-4=5
9-4=5 -> 9=4-5
9-4=7 -> 3+4=7
9-4=9 -> 5+4=9
9-5=1 -> 6-5=1
9-5=4 -> 9=5-4
9-5=6 -> 9-3=6
9-5=8 -> 3+5=8
9-5=8 -> 9-9=0
9-5=9 -> 8-5=3
9-6=0 -> 6-6=0
9-6=0 -> 9-9=0
9-6=2 -> 9-6=3
9-6=3 -> 9=6-3
9-6=5 -> 9-6=3
9-6=7 -> 9-8=1
9-6=9 -> 9-0=9
9-6=9 -> 3+6=9
9-7=0 -> 9-1=8
9-7=2 -> 9=7-2
9-7=3 -> 9-7=2
9-7=6 -> 9-1=8
9-7=7 -> 8-1=7
9-7=7 -> 8-7=1
9-7=9 -> 9-1=8
9-8=1 -> 9=8-1
9-8=2 -> 8-6=2
9-8=3 -> 9-0=9
9-8=5 -> 9-0=9
9-8=8 -> 8-0=8
9-8=8 -> 8-8=0
9-8=9 -> 9+0=9
9-9=0 -> 9=9-0
9-9=3 -> 9-6=3
9-9=3 -> 8-5=3
9-9=5 -> 8-3=5
9-9=6 -> 9-9=0
9-9=7 -> 9-8=1
9-9=9 -> 9-0=9
9-9=9 -> 9-9=0
Mam nadzieję, że nie ma jakich drastycznych błędów w rozwiązaniu?
W ramach ćwiczeń polecam każdemu chętnemu rozwiązanie tego problemu w Waszym ulubionym języku programowania. Jeżeli twierdzicie, że da się prościej to zapewne macie racje, ale zawsze miło mi zobaczyć Wasz kod.