Archiwum: November, 2016

Prosty problem jako zadanie z programowania

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:

img_1110

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:

screenshot_43

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:

screenshot_44

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.

| Komentarze

Zdjęcia z kilku ostatnich lotów dronem

Drony pozwalają na spojrzenie na świat dookoła nas z innej perspektywy. W przeciągu ostatnich tygodni latałem kilkanaście razy i chciałem podzielić się z Wami zdjęciami, które udało mi się zrobić. Znad Wisły w okolicy mostu Świętokrzyskiego zrobiłem kila ujęć obydwu stron Warszawy:

dji_0010-hdrdji_0015-hdrdji_0069-hdr

Wyjątkowo ładny efekt udało mi się osiągnąć poprzez połączenie 5 ujęć (wszystkie HDR) w panoramę:

dji_0039-hdr-pano

Niewiele dalej na północ, przy moście Gdańskim zrobiłem kilka ujęć Warszawy w nocy:

dji_0005dji_0021dji_0049

Jeden dzień był wyjątkowo pochmurny, ale okazało się, że całkiem blisko świeci słońce:

dji_0195-hdrdji_0219-hdrdji_0226-hdr

Z lotu w chmury mam również film:

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!

| Komentarze

GMT – piękne mapy w kilku krokach

GMT (Generic Mapping Toolbox) to zestaw narzędzi do pracy z danymi geograficznymi (i nie tylko) opracowany na uniwersytecie na Hawajach (o mało co nie przyjęli mnie tam do pracy nad tym projektem… ehhh…), o którym mało kto pewnie słyszał. Podstawowym zastosowaniem GMT jest tworzenie różnego rodzaju map. Na stronie domowej projektu opisana jest instalacja (Windows, Mac OS X, Linux) oraz udostępniona jest obszerna dokumentacja, więc nie będę tych rzeczy tutaj powtarzał. Chciałem Was zachęcić kilkoma prostymi przykładami do zapoznania się z tym pakietem. Uwaga: to nie jest pełny tutorial, to tylko zachęta!

Przykład 1: Mapa bazowa Europy. GMT jest oczywiście pakietem bez interfejsu graficznego, więc wszystko odbywa się z wiersza poleceń:

gmt psbasemap -R-15/35/30/72 -Jl9/11/45/70/1:40000000 -B5g2:."Europa": -K -P -Y10 > Europa.ps
gmt pscoast -R -Di -Ia/0.03p,black -J -N1/0.5p,- -G#dde6d5 -S#a5bfdd -A0/0/4 -W0.5p,black -O -P >> Europa.ps
convert -geometry 2048x2048 -density 300 -trim Europa.ps Europa.png


Pierwszą komendą tworzymy mapę bazową o podanym wymiarze (-R), w projekcji azymutalnej równopowierzchniowej Lamberta (-Jl). GMT wynik (w postaci języka post script) wypisuje na standardowe wyjście. Drugą komendą rysujemy na mapie linię brzegową i granice państw. Opcja -D odpowiada za dokładność granic, -I za rysowanie rzek, -G i -S za kolory morza i lądów itd. (wszystko dokładnie opisane w dokumentacji). Trzecią komendą konwertujemy ps na png. Oto wynik:

europa

Przykład 2: Mapa bazowa Europy z naniesionymi punktami. Sama mapa bazowa jest ładna, ale mało użyteczna. Często chcemy na mapie pokazać jakieś lokalizacje. Na przykład lotniska. Ze strony http://openflights.org/data.html#airport pobrałem plik z listą lotnisk na świecie i przygotowałem mapę:

awk -F, '{print $8 "\t" $7}' airports.dat > airports.txt
gmt psbasemap -R-15/35/30/72 -Jl9/11/45/70/1:40000000 -B5g2:."Airports in Europe": -K -P -Y10 > Europa_air.ps
gmt pscoast -R -Di -J -N1/0.5p,- -G#dde6d5 -S#a5bfdd -A0/0/1 -W0.5p,black -O -P -K >> Europa_air.ps
gmt psxy airports.txt -Jl -O -R -Sc0.1c -W -G255/0/0 -K >> Europa_air.ps
convert -geometry 2048x2048 -density 300 -trim Europa_air.ps Europa_air.png


W pierwszej linii konwertują plik z danymi lotnisk na listę współrzędnych geograficznych. Drugą i trzecią linię już znamy. W czwartej linii rysuję na mapie czerwone kółeczka w każdej z zapisanych lokalizacji. Oto wynik:

europa_air

Przykład 3: Mapa topograficzna Polski. Jako przykład prezentacji rozkładu przestrzennego zmiennej wybrałem wysokość terenu nad poziomem morza. Miałem przygotowany wcześniej plik tekstowy zawierający informacje o wysokości n.p.m.:

screenshot_42

Teraz wystarczy tylko przeliczyć te dane na siatkę geograficzną i nanieść na mapę:

gmt surface DEM.xyz -R13.8/24.5/48.7/55 -I0.02/0.01 -GDEM.grd -T0.5 -C0.01
gmt psbasemap -R13.8/24.5/48.7/55 -Jl19.20/19.30/52/60/1:5000000 -B2g2:."Polska - Mapa Topo": -K -P -Y10 > PL.ps
gmt makecpt -CDEM_PL_wiki.cpt -T-10/2500/1 > tmp.cpt
gmt psscale -D3.1i/-.5i/7.0i/0.5ih -Ctmp.cpt -B250::/:m: -O -K -P >> PL.ps
gmt grdimage -Bg DEM.grd -R -J -O -Q -K -nn+a -Ctmp.cpt >> PL.ps
gmt pscoast -R  -Ia -Df -J -N1/0.5p,- -S#a5bfdd -A0/0/4 -W0.25p,black -K -O -P -L23.7/49.1/54/100+u -T14.2/54.5/1.5:: >> PL.ps
convert -geometry 2048x2048 -density 300 -trim PL.ps PL.png

W pierwszej linii przeliczamy dane w punktach na powierzchnię w siatce geograficznej i zapisujemy do pliku grd. W drugiej linii tworzymy mapę bazową dla Polski. Trzecia linia to przygotowanie skali kolorów na podstawie wzorca (DEM_PL_wiki.cpt, dużo wzorców tutaj). Czwarta linia to dodanie do mapy skali kolorów. Piąta linia to naniesienie na mapę naszej powierzchni w kolorach zgodnych ze skalą. Szósta linia to nałożenie na wszystko granic i zamalowanie wody na niebiesko. Oto efekt:

pl

GMT ma duże możliwości, niestety kosztem łatwości obsługi. Ważną zaletą jest tworzenie map w postaci wektorowej. GMT polecałbym szczególnie w dwóch przypadkach: po pierwsze gdy potrzebujemy bardzo wysokiej jakości np. do publikacji i po drugie w przypadku, gdy chcemy proces automatyzować np. generując indywidualne mapy dla użytkowników itp.

Gdybyście mieli jakieś pytania, piszcie – używam GMT od 5 lat więc może będę mógł pomóc.

| Komentarze

Sprytne omijanie limitu zapytań z adresu IP

Dawno dawno temu podjąłem się parsowania danych z pewnej strony www, na której obowiązywał limit ilości zapytań z jednego adresu IP. Działało to mniej więcej tak, że po kilku zapytaniach dany adres IP był blokowany na kilka minut. Ponieważ do pobrania było bardzo dużo rekordów, filtr ten skutecznie mnie blokował. Zacząłem szukać rozwiązania i po kilku iteracjach doszedłem do prostego i relatywnie taniego sposobu.

Na rynku istnieje wielu dostawców usługi zwanej Hosting SEO (do pozycjonowania) – od zwykłego hostingu różni się możliwością wykorzystania wielu adresów IP w ramach jednego konta. Adresy IP są stosunkowo drogie (jeśli potrzebujemy kilkadziesiąt), ale w hostingu SEO ta sama pula adresów dostępna jest dla wielu kont klientów. Za konto z pulą 32 adresów IP zapłacimy rzędu 20-30 zł za miesiąc. Po wykupieniu usługi i zalogowaniu się do panelu admina powinniśmy mieć dostęp do listy “naszych” adresów ip.

Zanim przejdziemy do rozwiązania, zróbmy prosty test. Spróbujmy z poziomu serwera sprawdzić adres IP. Zrobimy to za pomocą prostego skryptu php:

<?php
	$ch = curl_init('http://whatismyip.org/');
	curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
	$myIp = curl_exec($ch);
	preg_match_all('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$myIp, $out);
	print_r($out);
?>

Skrypt uruchamiamy przez przeglądarkę (tani serwer raczej nie oferuje dostępu przez ssh). Wynik powinien wyglądać mniej więcej tak:

Array
(
    [0] => Array
        (
            [0] => 212.59.244.5
        )

)

Adres IP, który się wyświetli jest najprawdopodobniej przypisany domyślnie do naszego konta. Możemy przez panel administratora go zmienić i spróbować ponownie. Niestety takie rozwiązanie nie pomoże nam w szybkim zmienianiu adresów. Okazuje się, że wystarczy przy wywoływaniu CURL dodać informację, z którego interfejsu sieciowego serwera chcemy skorzystać. Na liście dostępnych dla mojego konta adresów IP były 32 pozycje:

ip

Użyjmy pierwszego adresu z listy:

<?php
	$ch = curl_init('http://whatismyip.org/');
	curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
	curl_setopt($ch,CURLOPT_INTERFACE,"31.6.69.41");
	$myIp = curl_exec($ch);
	preg_match_all('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$myIp, $out);
	print_r($out);
?>

Otrzymujemy wynik:

Array
(
    [0] => Array
        (
            [0] => 31.6.69.41
        )

)

Działa! Za pomocą CURL możemy zdziałać cuda. Wystarczy tylko dopisać logikę, która losowo będzie przy każdym zapytaniu zmieniała adresy IP.

Jeśli ktoś jest dociekliwy to może odkryć jeszcze jedną rzecz. Wykupując najtańsze konto (z najmniejszą liczbą adresów IP) będziemy działali prawdopodobnie na tej samej maszynie co właściciele droższych pakietów. Możemy więc korzystać z całej puli adresów IP przypisanych do tego serwera, nawet jeśli nie są dla nas dostępne w panelu. U mnie działały np. takie adresy spoza listy: 31.6.69.42, 31.6.69.43, 31.6.69.45, 31.6.69.47 (więcej nie próbowałem). Użycie adresu nie przypisanego do danej maszyny spowoduje zwrócenie pustego stringa – nasze dane nie wiedziały jak wrócić.

Trzeba pamiętać jeszcze o małej małych zasobach serwera dla takiego konta hostingowego – jeśli zaczniemy generować za duże obciążenie prędzej czy później odezwie się admin z pretensjami.

Może komuś z Was się takie coś kiedyś przyda.

| Komentarze

Reprezentacja czasu w komputerze

Czas jest jednym z podstawowych “parametrów” świata, który na co dzień wielokrotnie zapisujemy (zapamiętujemy) i przeliczamy. Obecnie oznacza to, że jest przetwarzany przez komputery. Mogło by się wydawać, że sprawa jest prosta, ale od razu widać całą masę kłopotów. Pomysł na ten wpis wynika z bezsensownej zmiany czasu na zimowy w minioną niedzielę.

Po pierwsze strefy czasowe. Dopóki znajdujemy się w jednej strefie czasowej wszystko jest ok, potrafimy bez problemu obliczyć różnicę czasu pomiędzy dwiema datami. Do czasu: mamy przecież w Polsce dwie strefy czasowe – letnią i zimową. Jeżeli interesuje nas dokładne obliczenie czasu to musimy te informacje uwzględniać. Rozwiązaniem tego problemu może być zapamiętywanie i obliczanie dat i czasów w UTC (Universal Time Coordinated). Wiedzę na temat aktualnej w danym momencie i miejscu strefie czasowej najlepiej pobierać z systemu operacyjnego, który powinien to “ogarniać”. Nie warto tego rozgryzać samemu, bo ilość wyjątków jest powalająca. W tym miejscu polecam film The Problem with Time & Timezones.

Do tej pory pamiętam kartkówkę z geografii w liceum na której liczyliśmy czas podróży samolotem z San Francisco to Tokio (i z powrotem) na podstawie godzin przylotów i odlotów. Jedynym rozwiązaniem gwarantującym dobry wynik było przejście do czasu UTC.

Po drugie format zapisu danych. Komputer operuje na liczbach a ludzie na ciągach znaków. Istnieje wiele formatów zapisu daty i czasu w formie ciągu znaków, ale to jest tylko reprezentacja “dla ludzi”. W pamięci data i czas mogą być zapisane na wiele sposobów, z których trzy są moim zdaniem najważniejsze:

  • Możemy pamiętać oddzielnie jako liczby całkowite rok, miesiąc, dzień, godzinę, minutę, sekundę i milisekundę (albo nanosekundę itp.) + dodatkowo kod strefy czasowej (zapisany np. w postaci liczby minut w stosunku do UTC). Taki sposób jest dokładny i uniwersalny, ale mało wydajny i pamięciożerny. Ktoś jeszcze pamięta problem roku 2000?
  • Drugim sposobem jest zapisywanie epoki unix, czyli ilości sekund od 01-01-1970. Tu problemy są dwa: stosując int32 mamy bardzo ograniczony zakres dat i dokładność na poziomie sekundy.
  • Trzecim sposobem, z którym spotykam się na co dzień jest zapisywanie liczby zmiennoprzecinkowej określającej dni od roku 0. Z tej metody korzysta Matlab. Zdecydowaną zaletą jest możliwość opisania dowolnej daty w historii i przyszłości wszechświata oraz możliwość uwzględniania dokładności sub-sekundowej. Wada tego zapisu wynika ze zmiennej dokładności zapisu zmiennoprzecinkowego w zależności od wartości – pisałem o tym kilka tygodni temu.

Zdecydowanie trzeci sposób zapisu daty jest moim ulubionym – pozwala na bardzo szybkie operowanie na datach, bez utraty precyzji. Operacje na liczbach zmiennoprzecinkowych są całkowicie rutynowe, więc ciężko będzie znaleźć coś wydajniejszego.

Bardzo wiele zależy od języka programowania, w każdym jest inne podejście. Szczególnie w Pythonie widać ogrom komplikacji. Jakie są Wasze doświadczenia? Z którego formatu korzystacie najczęściej?

Jako bonus chciałbym Wam pokazać 3 animacje mojego autorstwa. Pierwsza jest dość przewidywalna bo pokazuje czas trwania dnia w zależności od dnia roku:

Dwie kolejne są już dużo ciekawsze, ponieważ pokazują czas wschodu i zachodu słońca w lokalnej strefie czasowej.

Widać wyraźnie zmianę czasu letniego na zimowy (w różnym czasie zależnie od regionu) i słabe dopasowanie strefy czasowej w niektórych regionach.

| Komentarze