Compare commits

..

188 Commits

Author SHA1 Message Date
c9820b2a81 Delete src/measure_time.py 2026-05-30 15:00:19 +00:00
d65c18eadb Delete src/linked_list.py 2026-05-30 15:00:14 +00:00
8a54fc4485 Delete src/hash_table.py 2026-05-30 15:00:10 +00:00
fb367515f8 Delete src/bst.py 2026-05-30 15:00:05 +00:00
d25d91e54d [1] add report for 1-st laba 2026-05-24 21:26:28 +00:00
58ca1450e3 [1] add png to report 2026-05-24 21:25:39 +00:00
d47b06ae6f [1] add csv results and png plots 2026-05-24 21:24:27 +00:00
74c13ec0fb [1] add performance experiments and plotting 2026-05-24 21:16:18 +00:00
319171fe2c [1] implement binary search tree 2026-05-24 21:14:28 +00:00
0c2e487472 [1] add hash table implementation 2026-05-24 21:13:07 +00:00
e464e4e1e0 [1] add main.py 2026-05-24 21:11:54 +00:00
05980b5e12 Update semyanovra/docs/data/2-nd/maze/no_exit10x10.txt 2026-05-24 21:08:10 +00:00
350ccb861e Update semyanovra/docs/data/2-nd/maze/medium10x10.txt 2026-05-24 21:07:39 +00:00
766b3facf2 Update semyanovra/docs/data/2-nd/maze/large20x20.txt 2026-05-24 21:07:07 +00:00
bebedf835e Update semyanovra/docs/data/2-nd/maze/level1.txt 2026-05-24 21:06:45 +00:00
ee9aedf359 Update semyanovra/docs/data/2-nd/maze/empty15x15.txt 2026-05-24 21:06:16 +00:00
b9a3497b5a Update semyanovra/docs/data/2-nd/mazelarge20x20.txt 2026-05-24 21:05:44 +00:00
86190175eb Update semyanovra/docs/data/maze/large20x20.txt 2026-05-24 21:05:20 +00:00
29a062508f Update semyanovra/docs/data/maze/empty15x15.txt 2026-05-24 21:04:57 +00:00
f48e920dd1 [2] update structure of repo 2026-05-24 21:04:12 +00:00
536c36b9fb Delete semyanovra/scr/generate_data.py 2026-05-24 21:03:05 +00:00
35ab0b7347 Delete semyanovra/docs/experiment_setup.md 2026-05-24 21:02:52 +00:00
163eb1010f [2] add report 2026-05-24 15:10:38 +00:00
1a9317dad7 [2] add experimental data 2026-05-24 15:06:38 +00:00
d4aa0588c3 [2] add maze no exit maze 2026-05-24 15:02:58 +00:00
3773442642 [2] add maze_empty 2026-05-24 15:01:34 +00:00
af06031de7 [2] add maze20x20 2026-05-24 15:01:06 +00:00
22a395ad0a [2] add 10x10 maze 2026-05-24 15:00:34 +00:00
bbdb44dd88 [2] add maze1 2026-05-24 15:00:00 +00:00
82943137b3 [2] feat - эксперименты и построение графиков 2026-05-24 14:58:06 +00:00
bca3eb1b7c [2] feat - полноценный игровой цикл и интеграция алгоритмов 2026-05-24 14:56:28 +00:00
b29a0309a1 [2] feat - игрок и система команд с Undo 2026-05-24 14:55:50 +00:00
2e68f1a389 [2] feat - Observer и консольная визуализация 2026-05-24 14:54:57 +00:00
a966076c00 [2] feat - оркестратор MazeSolver и статистика 2026-05-24 14:08:34 +00:00
acbd3a3892 [2] feat - три алгоритма поиска пути (BFS, DFS, A*) 2026-05-24 14:07:25 +00:00
735626045b [2] feat - загрузка лабиринта из файла 2026-05-24 14:06:39 +00:00
56ff7f317a [2] init 2026-05-24 14:06:06 +00:00
b367852062 Пункт 3: реализованы структуры данных (LinkedList, HashTable, BST) 2026-05-15 18:43:27 +03:00
f42be3b51a добавлен пункт 2: интрументы замера времени 2026-05-12 21:17:40 +03:00
6716bd1d0b Add data generation 2026-04-08 22:01:28 +03:00
6e4ae1835b Merge pull request '[1] initial commit' (#171) from osipovamd/2026-rff_mp:osipovamd into develop
Reviewed-on: UNN/2026-rff_mp#171
2026-04-05 10:54:06 +00:00
MariiaOs
25341dc814 [1] initial commit 2026-04-04 17:13:00 +03:00
405d1e583b Merge pull request '[0] initial commit' (#162) from varnakovaa/2026-rff_mp:VarnakovAA into develop
Reviewed-on: UNN/2026-rff_mp#162
2026-04-04 09:12:00 +00:00
74807f5514 Обновить README.md 2026-04-04 07:11:52 +00:00
e4c7e2d97a [0] initial commit 2026-03-23 21:32:01 +03:00
f5b0fec46f Merge pull request '[1] FINISH 1-st-exercise' (#148) from IvanBud123/2026-rff_mp:1-st-exercise into develop
Reviewed-on: UNN/2026-rff_mp#148
2026-03-15 06:42:22 +00:00
e4423a3be8 Merge pull request '[0] initial commit' (#154) from BoriskovaDV/2026-rff_mp:BoriskovaDV into develop
Reviewed-on: UNN/2026-rff_mp#154
2026-03-15 06:41:55 +00:00
eeb28d8b6a [0] init 2026-03-15 05:18:35 +00:00
b3e4b6149e docs 2026-03-15 02:46:48 +00:00
c98c8a472b Merge pull request '[0] initial commit' (#152) from victorovaas/2026-rff_mp:victorovaas into develop
Reviewed-on: UNN/2026-rff_mp#152
2026-03-14 09:27:51 +00:00
Git Version Control
666e6ecd41 [1] ... README.md 2026-03-12 04:02:58 +00:00
062d3b983e Merge pull request '[0] initial commit' (#151) from KislyuninED/2026-rff_mp:KislyuninED into develop
Reviewed-on: UNN/2026-rff_mp#151
2026-03-10 08:17:33 +00:00
197c266cd4 [0] initial commit 2026-03-07 17:15:56 +03:00
b874ac28aa Merge pull request '[0] initial commit' (#149) from SavelevMI/2026-rff_mp:SavelevMI into develop
Reviewed-on: UNN/2026-rff_mp#149
2026-03-06 10:15:37 +00:00
4cef8060a3 [15] FINISH for 1-st exersize 2026-03-04 01:40:38 +03:00
50798eb572 Merge branch 'develop' into 1-st-exercise 2026-03-04 01:37:44 +03:00
7067bfa12a [14] FINISH for 1-st exersize 2026-03-04 01:14:58 +03:00
64f43373df [13] FINISH for 1-st exersize 2026-03-04 01:02:42 +03:00
da65d02bd7 [12] BIG UPDATE 2026-03-04 01:02:40 +03:00
0441c5076a [11] A minor update that fixes errors and typos 2026-03-04 01:02:37 +03:00
28de33a83d [10] A minor update that fixes errors and typos 2026-03-04 01:02:32 +03:00
49e91066d4 [9] finish with adding BST(Binar Search Tree) 2026-03-04 01:02:29 +03:00
bb28c3dd2f [8] start adding binar search func(create_node and bst_insert) 2026-03-04 01:02:26 +03:00
1ebec4223a [7] add hash func and tests of hash func 2026-03-04 01:02:23 +03:00
e868e94fcd [6] add hash func find, insert and hash_func 2026-03-04 01:02:17 +03:00
0e84534109 [5] modyfi LinkedListBook for 2-nd exersize 2026-03-04 01:02:11 +03:00
8c642fea20 [4] start coding hash-table 2026-03-04 00:56:59 +03:00
7dbd3075b6 [3] adding ll_list_all 2026-03-04 00:56:59 +03:00
050462d011 [2] adding ll_find and ll_delete 2026-03-04 00:56:59 +03:00
ba244f24bb [1] implemented the ll_insert function for an first exercise 2026-03-04 00:56:59 +03:00
39fe26b3f1 [0] initial commit 2026-03-04 00:56:59 +03:00
5de2cca73e Обновить README.md 2026-03-04 00:56:59 +03:00
8c17e92dd8 Удалить testfile.txt 2026-03-04 00:56:59 +03:00
b1cb2491d8 Обновить README.md 2026-03-04 00:56:59 +03:00
a5ee4d9ae5 Обновить README.md 2026-03-04 00:56:59 +03:00
fb8f0c47f5 Обновить README.md 2026-03-04 00:56:59 +03:00
099ec9f5e3 Обновить README.md 2026-03-04 00:56:59 +03:00
3f89175175 Merge pull request '[0] initial commit' (#144) from starikovta/2026-rff_mp:starikovta into develop
Reviewed-on: UNN/2026-rff_mp#144
2026-03-01 13:45:14 +00:00
3360e2dc8f Merge pull request '[0] initial commit' (#140) from MochalovAE/2026-rff_mp:MochalovAE into develop
Reviewed-on: UNN/2026-rff_mp#140
2026-03-01 13:44:44 +00:00
91b3c9a007 Merge pull request '[0] initial commit' (#138) from famutdinovmd/2026-rff_mp:famutdinovmd into develop
Reviewed-on: UNN/2026-rff_mp#138
2026-03-01 13:44:31 +00:00
44552108d4 [0] initial commit 2026-02-28 17:53:25 +03:00
ff1063d866 [0] initial commit 2026-02-28 17:19:44 +03:00
38d4ae0d5e [0] initial commit 2026-02-28 16:42:45 +03:00
7adc1e91f5 [0] initial commit 2026-02-28 14:21:18 +03:00
d2b04e6f0e Merge pull request '[0] initial commit' (#137) from sorokinad/2026-rff_mp:SorokinAD into develop
Reviewed-on: UNN/2026-rff_mp#137
2026-02-28 10:59:40 +00:00
0206cfd65f Merge pull request '[0] initial commit' (#135) from SokolovNE/2026-rff_mp:SokolovNE into develop
Reviewed-on: UNN/2026-rff_mp#135
2026-02-28 10:58:21 +00:00
89f2fa1162 Merge pull request '[0]develop' (#136) from Ezhovnd/2026-rff_mp:develop into develop
Reviewed-on: UNN/2026-rff_mp#136
2026-02-28 10:58:02 +00:00
Морозов Никита С
7278f7d9db Merge branch 'ezhovnd' into develop 2026-02-28 13:55:21 +03:00
Сорокин Александр Д
14272a7c25 [0] initial commit 2026-02-28 13:53:01 +03:00
Морозов Никита С
e613581d34 [0] initial com mit 2026-02-28 13:44:09 +03:00
cdfdb49be1 [0] initial commit 2026-02-28 13:34:18 +03:00
52c001a380 Merge pull request '[0] initial commit' (#134) from nikitovie/2026-rff_mp:nikitovie into develop
Reviewed-on: UNN/2026-rff_mp#134
2026-02-28 10:21:56 +00:00
9dceb29513 [0] initial commit 2026-02-28 13:09:29 +03:00
f409499159 Merge pull request '[0] initial commit' (#133) from gutovvm/2026-rff_mp:GutovVM into develop
Reviewed-on: UNN/2026-rff_mp#133
2026-02-28 09:52:33 +00:00
59076d3df1 Merge pull request '[0] initial commit' (#132) from KolbasovPD/2026-rff_mp:KolbasovPD into develop
Reviewed-on: UNN/2026-rff_mp#132
2026-02-28 09:52:21 +00:00
3dbf137752 Merge pull request '[0] initial commit' (#128) from gorkinmm/2026-rff_mp:GorkinMM into develop
Reviewed-on: UNN/2026-rff_mp#128
2026-02-28 09:52:12 +00:00
918536d2ed Merge pull request '[0] initial commit' (#114) from groshevava/2026-rff_mp:groshevava into develop
Reviewed-on: UNN/2026-rff_mp#114
2026-02-28 09:52:01 +00:00
812f0acac2 Merge pull request '[0] initial commit' (#105) from KuznetsovMA/2026-rff_mp:KuznetsovMA into develop
Reviewed-on: UNN/2026-rff_mp#105
2026-02-28 09:51:40 +00:00
8a30aff913 Merge pull request '[0] initial commit' (#103) from zhigalovrd/2026-rff_mp:zhigalovrd into develop
Reviewed-on: UNN/2026-rff_mp#103
2026-02-28 09:51:17 +00:00
d157503ef2 Merge pull request '[0] initial commit' (#120) from shapovalovka/2026-rff_mp:shapovalovka into develop
Reviewed-on: UNN/2026-rff_mp#120
2026-02-28 09:51:03 +00:00
8d4e9ebeca Merge pull request '[0] initial commit' (#112) from kuznetsovTD/2026-rff_mp:kuznetsovTD into develop
Reviewed-on: UNN/2026-rff_mp#112
2026-02-28 09:50:50 +00:00
ec9b3fb7c6 Merge pull request '[0] initial commit' (#111) from bolonkinnm/2026-rff_mp:BolonkinNM into develop
Reviewed-on: UNN/2026-rff_mp#111
2026-02-28 09:50:07 +00:00
a29c7d7af6 Merge pull request '[0] initial commit' (#110) from ShulpinIN/2026-rff_mp:ShulpinIN into develop
Reviewed-on: UNN/2026-rff_mp#110
2026-02-28 09:49:54 +00:00
ef189db30c Merge pull request '[0] initial commit' (#117) from SokolovEN/2026-rff_mp:SokolovEN into develop
Reviewed-on: UNN/2026-rff_mp#117
2026-02-28 09:49:44 +00:00
110e13b31e Merge pull request '[0] initial commit' (#118) from VildyaevAV/2026-rff_mp:VildyaevAV into develop
Reviewed-on: UNN/2026-rff_mp#118
2026-02-28 09:49:34 +00:00
5640088b5a Merge pull request '[0] initial commit' (#121) from ZhuravlevDV/2026-rff_mp:ZhuravlevDV into develop
Reviewed-on: UNN/2026-rff_mp#121
2026-02-28 09:49:19 +00:00
d2b26c6f9e Merge pull request '[0] initial commit' (#108) from borisovmi/2026-rff_mp:BorisovMI into develop
Reviewed-on: UNN/2026-rff_mp#108
2026-02-28 09:49:08 +00:00
a48c0abf0d Merge pull request '[0] initial commit' (#104) from soninrv/2026-rff_mp:soninrv into develop
Reviewed-on: UNN/2026-rff_mp#104
2026-02-28 09:48:56 +00:00
6ec811b963 Merge pull request '[0] initial commit' (#116) from SobolevNS/2026-rff_mp:SobolevNS into develop
Reviewed-on: UNN/2026-rff_mp#116
2026-02-28 09:48:38 +00:00
5fc3c42694 Merge pull request '[0] initial commit' (#113) from rybakovaa/2026-rff_mp:rybakovaa into develop
Reviewed-on: UNN/2026-rff_mp#113
2026-02-28 09:48:25 +00:00
16808ba847 Merge pull request '[0] initial commit' (#119) from agafonovdm/2026-rff_mp:agafonovdm into develop
Reviewed-on: UNN/2026-rff_mp#119
2026-02-28 09:47:52 +00:00
e19c3b7841 Merge pull request '[0] initial commit' (#122) from larikovaaa/2026-rff_mp:LarikovaAA into develop
Reviewed-on: UNN/2026-rff_mp#122
2026-02-28 09:47:35 +00:00
88aa5a412f Merge pull request '[0] initial commit' (#126) from lukovnikovde/2026-rff_mp:LukovnikovDE into develop
Reviewed-on: UNN/2026-rff_mp#126
2026-02-28 09:47:18 +00:00
61f966e163 Merge pull request '[0] initial commit' (#125) from Ridge/2026-rff_mp:KorotkinSE into develop
Reviewed-on: UNN/2026-rff_mp#125
2026-02-28 09:46:55 +00:00
8c4322f768 Merge pull request '[0] initial commit' (#127) from SolovevDD/2026-rff_mp:SolovevDD into develop
Reviewed-on: UNN/2026-rff_mp#127
2026-02-28 09:46:41 +00:00
b85b706169 Merge pull request '[0] initial commit' (#106) from KuznetsovYuM/2026-rff_mp:KuznetsovYuM into develop
Reviewed-on: UNN/2026-rff_mp#106
2026-02-28 09:46:28 +00:00
8198cfb061 Merge pull request '[0] initial commit' (#102) from petryaninyas/2026-rff_mp:petryaninyas into develop
Reviewed-on: UNN/2026-rff_mp#102
2026-02-28 09:42:49 +00:00
7b7e8e93d4 Merge pull request '[0] initial commit' (#100) from MarkinAM/2026-rff_mp:MarkinAM into develop
Reviewed-on: UNN/2026-rff_mp#100
2026-02-28 09:42:19 +00:00
036c08c2d9 [0] initial commit 2026-02-28 08:25:52 +03:00
2bf7ca3c92 [0] initial commit 2026-02-28 01:11:43 +03:00
786881334d [0] initial commit 2026-02-27 23:43:40 +03:00
SolovevDD
ddf8ef5105 [0] initial commit 2026-02-27 23:37:07 +03:00
ab854a04dd [0] initial commit 2026-02-27 23:32:09 +03:00
c121e51b1f [0] initial commit 2026-02-27 22:13:03 +03:00
3e175eb367 Добавил папку SavelevMI 2026-02-27 20:38:31 +03:00
3e5ee4a5a4 [0] initial commit 2026-02-27 20:36:00 +03:00
aa6fbb0692 [0] initial commit 2026-02-27 19:17:17 +03:00
shapovalovka
9eedcecf0c [0] initial commit 2026-02-27 19:14:53 +03:00
2204ca3dc8 [0] initial commit 2026-02-27 18:55:31 +03:00
764e6a3a2c [0] initial commit 2026-02-27 18:00:19 +03:00
ec48b13150 [0] initial commit 2026-02-27 12:00:34 +03:00
3cfd61e6cc [0] initial commit 2026-02-27 09:09:05 +03:00
groshevava
57c811ece4 [0] initial commit 2026-02-26 23:04:47 +00:00
0d86929b62 [0] initial commit 2026-02-26 23:02:00 +03:00
e442988725 [0] initial commit 2026-02-26 22:53:18 +03:00
1a041a4dac [0] initial commit 2026-02-26 21:00:34 +03:00
af2f607a3b [0] fix: correct file extension to .md 2026-02-26 19:50:20 +03:00
96532a99fb [0] initial commit 2026-02-26 19:44:37 +03:00
809f768703 [0] initial commit 2026-02-26 13:38:56 +03:00
154b9b8b65 [0] initial commit 2026-02-26 13:34:48 +03:00
aeb608a5d3 [0] initial commit 2026-02-25 19:02:50 +03:00
f541180fcf [0] initial commit 2026-02-25 17:16:19 +03:00
134b330145 [0] initial commit 2026-02-25 14:17:27 +03:00
8dc1d57252 [0] initial commit 2026-02-24 23:07:54 +03:00
MarkinAM
d7a13c9fe6 [0] initial commit 2026-02-24 17:57:48 +03:00
8124ef4551 [1] docs: data structures 2026-02-24 14:04:28 +00:00
8744999b03 Merge pull request '[1] raskatovia' (#99) from raskatovia/2026-rff_mp:raskatovia-2 into develop
Reviewed-on: UNN/2026-rff_mp#99
2026-02-24 12:17:16 +00:00
01f0e5dd88 Merge pull request '[0] initial commit' (#97) from pogodinda/2026-rff_mp:pogodinda into develop
Reviewed-on: UNN/2026-rff_mp#97
2026-02-24 12:16:53 +00:00
Иван
0c990ece48 [1] second submission 2026-02-23 13:49:00 +03:00
Иван
9af52f3f7b [429] initial 2026-02-23 13:38:33 +03:00
Иван
9e70450c50 [1] fix 2026-02-22 21:16:21 +03:00
Иван
a7323e3762 [0] initial commit 2026-02-22 21:07:02 +03:00
365f830e05 [0] initial commit 2026-02-22 12:45:20 +03:00
7fe5c11997 Обновить .gitignore 2026-02-21 11:45:45 +00:00
969b9ee469 Merge pull request '[0]sobininaas' (#93) from sobininaas/2026-rff_mp:sobininaas into develop
Reviewed-on: UNN/2026-rff_mp#93
2026-02-21 11:45:04 +00:00
0c6a8bd4db Обновить .gitignore 2026-02-21 11:44:54 +00:00
b849e3a7bc Merge branch 'develop' of http://31.128.43.79:3000/UNN/2026-rff_mp into develop 2026-02-21 14:29:21 +03:00
123
2e0eec1543 [0] 2026-02-21 14:28:51 +03:00
17fc44d83e fixed conflict 2026-02-21 14:28:06 +03:00
4b24d66ab7 Удалить 428.md 2026-02-21 11:24:46 +00:00
123
48d37c6f77 [0] initial commit 2026-02-21 14:23:21 +03:00
4a26e7aa89 Merge pull request '[0]shekurovAA' (#94) from shekurovaa/2026-rff_mp:shekurovAA into develop
Reviewed-on: UNN/2026-rff_mp#94
2026-02-21 11:15:41 +00:00
e0eb3ae13a Merge pull request '[0]' (#95) from fomichevks/2026-rff_mp:fomichevks into develop
Reviewed-on: UNN/2026-rff_mp#95
2026-02-21 11:15:29 +00:00
f983a6fa1d [0] 2026-02-21 14:13:16 +03:00
Смирнов Андрей Д
356e4b53dd [0] initial commit 2026-02-21 14:11:17 +03:00
b7cea24df4 Merge pull request '[0] initial commit' (#89) from smirnovad/2026-rff_mp:smirnovad into develop
Reviewed-on: UNN/2026-rff_mp#89
2026-02-21 11:05:41 +00:00
8221562750 Merge pull request '[0] initial commit' (#88) from semyanovra/2026-rff_mp:semyanovra into develop
Reviewed-on: UNN/2026-rff_mp#88
2026-02-21 11:05:32 +00:00
123
789d8ef3bb [0] 2026-02-21 14:04:51 +03:00
Смирнов Андрей Д
6e433a36ae [0] initial commit 2026-02-21 14:04:28 +03:00
123
a50fd0bfc1 [0] 2026-02-21 14:03:15 +03:00
Семьянов Руслан А
de2b98bc01 [0] initial commit 2026-02-21 13:58:46 +03:00
c53b093c38 Merge pull request '[0] initial commit' (#87) from kornevma/2026-rff_mp:kornevma into develop
Reviewed-on: UNN/2026-rff_mp#87
2026-02-21 10:55:06 +00:00
dd3b126d62 Merge pull request '[0] initial commit' (#85) from agapovads/2026-rff_mp:agapovads into develop
Reviewed-on: UNN/2026-rff_mp#85
2026-02-21 10:53:01 +00:00
048b211945 Merge pull request '[0] initial commit' (#84) from nehoroshevaa/2026-rff_mp:nehoroshevaa into develop
Reviewed-on: UNN/2026-rff_mp#84
2026-02-21 10:51:40 +00:00
6d05295507 Merge pull request '[0] initial commit' (#81) from anikinvd/2026-rff_mp:anikinvd into develop
Reviewed-on: UNN/2026-rff_mp#81
2026-02-21 10:50:19 +00:00
55d8ed29c6 Merge pull request '[0] initial commit' (#80) from meosyam/2026-rff_mp:meosyam into develop
Reviewed-on: UNN/2026-rff_mp#80
2026-02-21 10:49:39 +00:00
af2d62a446 Merge pull request '[0] initial commit' (#79) from novikovsd/2026-rff_mp:novikovsd into develop
Reviewed-on: UNN/2026-rff_mp#79
2026-02-21 10:49:22 +00:00
fe5a8bea89 Merge pull request '[0] initial commit' (#77) from duznb/2026-rff_mp:duznb into develop
Reviewed-on: UNN/2026-rff_mp#77
2026-02-21 10:49:12 +00:00
Волков Илья М
8fb5738b50 [0] initial commit 2026-02-21 13:48:49 +03:00
Новиков Семён Д
90503c7b92 [0] initial commit 2026-02-21 13:39:09 +03:00
Аникин Владислав Д
bcde039ddd [0] initial commit 2026-02-21 13:38:46 +03:00
ddc6d9b877 [0] initial commit 2026-02-21 13:36:14 +03:00
e1e08990e0 [0] initial commit 2026-02-21 13:36:01 +03:00
Агапова Дарина С
fe50036baf [0] initial commit 2026-02-21 13:35:45 +03:00
123
8f2e46907f [0] initial commit 2026-02-21 13:35:11 +03:00
Шекуров Алексей А
afa3504609 [0] initial commit 2026-02-21 13:34:52 +03:00
Корнев Макар А
ccb7c7a5da [0] initial commit 2026-02-21 13:32:10 +03:00
aca0eb0c84 Добавлен файл группы 427 для Погодин 2026-02-14 00:56:21 +03:00
72 changed files with 2227 additions and 2 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ __pycache__/
# C extensions # C extensions
*.so *.so
.DS_Store .DS_Store
# Distribution / packaging # Distribution / packaging
.Python .Python

0
BolonkinNM/426.md Normal file
View File

0
BoriskovaDV/428.md Normal file
View File

0
BorisovMI/429.md Normal file
View File

View File

@ -0,0 +1,457 @@
head = None
#node1 = {'name' : 'Ivan', 'phone' : '123-456', 'next' : None}
#head = node1
#node2 = {'name' : 'Dima', 'phone' : '789-123', 'next' : None}
#node1['next'] = node2
def ll_insert(head, name, phone):
curent = head
while curent is not None:
if curent['name'] == name:
curent['phone'] = phone
return head
curent = curent['next']
n_node = {'name' : name, 'phone' : phone, 'next' : None}
if head is None:
return n_node
curent = head
while curent['next'] is not None:
curent = curent['next']
curent['next'] = n_node
return head
print("====== TESTING ll_insert FUNC ========")
head = ll_insert(head,'Ivan','123-456')
print(head)
head = ll_insert(head, 'Boris', '123-456')
print(head)
head = ll_insert(head, 'Ivan', '321-654')
print(head)
head = ll_insert(head, 'Dima', '345-678')
print(head)
head = ll_insert(head, 'Boris', '111-222')
print(head)
head = ll_insert(head, 'Methody', '221-112')
head = ll_insert(head, 'Kiril', '112-221')
print(f"======= END TEST =======\n\n\n")
def ll_find(head, name):
curent = head
while curent is not None:
if curent['name'] == name:
return curent['phone']
curent = curent['next']
return None
print("====== TESTING ll_find FUNC ======")
print("Ivan`s phone: "+ ll_find(head, 'Ivan'))
print("Dima`s phone: "+ ll_find(head, 'Dima'))
print("Boris phone: "+ ll_find(head, 'Boris'))
print(f"====== END TEST ======\n\n\n")
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
curent = head['next']
while curent is not None:
if curent['name'] == name:
prev['next'] = curent['next']
return head
prev = curent
curent = curent['next']
return head
print("====== TEST ll_delete FUNC ======")
print("Del of Dima:", ll_delete(head, 'Dima'))
print("====== END TEST ======")
def ll_list_all(head):
records = []
curent = head
while curent is not None:
records.append((curent['name'],curent['phone']))
curent = curent['next']
records.sort(key=lambda pair: pair[0])
return records
print(f"\n\n\n\n")
print("====== TESTING ll_list_all FUNC ======")
print(ll_list_all(head))
print("====== END ======")
#============================== HASH FUNCTIONS =========================
SIZE = 5
buckets = [None] * SIZE
def hash_function(name, size):
return hash(name) % size
def ht_insert(buckets, name, phone):
index = hash_function(name, len(buckets))
head = buckets[index]
new_head = ll_insert(head, name, phone)
buckets[index] = new_head
return buckets
print(f"\n\n\n ====== TEST INSERT HASH ======")
print(buckets)
ht_insert(buckets, "Ivan", "123-456")
print(buckets)
ht_insert(buckets, "Dima", "789-123")
print(buckets)
ht_insert(buckets, "Boris", "456-789")
print(buckets)
print("====== END TEST ======\n\n\n")
def ht_find(buckets, name):
index = hash_function(name, len(buckets))
head = buckets[index]
return ll_find(head, name)
print("====== TEST FIND HASH FUN ======")
print("find by name Ivan: ",ht_find(buckets, "Ivan"))
print("find by name Dima: ",ht_find(buckets, "Dima"))
print("find by name Boris: ", ht_find(buckets, "Boris"))
print("====== END TEST ======\n\n\n")
def ht_list_all(buckets):
all_records = []
for head in buckets:
current = head
while current is not None:
all_records.append((current['name'], current['phone']))
current = current['next']
all_records.sort(key=lambda x: x[0])
return all_records
print("====== TEST FUNC LIST ALL ======")
print(ht_list_all(buckets))
print("====== END TEST ======\n\n\n")
def ht_delete(buckets, name):
index = hash_function(name, len(buckets))
head = buckets[index]
new_head = ll_delete(head, name)
buckets[index] = new_head
return buckets
print("====== GLOBAL TEST FOR HASH BASED FUN ======")
buckets = [None] * 10
ht_insert(buckets, "Ivan", "123-456")
print(buckets)
ht_insert(buckets, "Boris", "789-012")
print(buckets)
ht_insert(buckets, "Anna", "345-678")
print(buckets)
ht_insert(buckets, "Ivan", "111-222") # update
print(buckets)
print("Find Ivan`s phone: ",ht_find(buckets, "Ivan")) # 111-222
print("Find Petr`s phone: ",ht_find(buckets, "Petr")) # None
# Удаляем
print("delite Boris from buckets")
ht_delete(buckets, "Boris")
print("search Boris = ",ht_find(buckets, "Boris")) # None
# Все записи
print("list all records: ",ht_list_all(buckets))
print("====== END GLOBAL TEST ======\n\n\n")
# ======================== TREE FUNC ====================
def create_node(name,phone):
return {'name': name, 'phone': phone, 'left': None, 'right': None}
print("====== START TREE FUNC CHAPTER ======\n\n")
print("====== TEST CREATE NODE FUNC ======")
root = create_node('Ivan', '123-456')
print("Create Ivan node: ",root)
print("====== END TEST ====== \n\n\n")
def bst_insert(root, name, phone):
if root is None:
return create_node(name, phone)
if name == root['name']:
root['phone'] = phone
elif name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
else:
root['right'] = bst_insert(root['right'], name , phone)
return root
print("====== TEST INSERT FUNC ======")
root = bst_insert(root, 'Dima', '456-789')
print("add Dima: ", root)
root = bst_insert(root, 'Boris', '789-123')
print("add Boris: ", root)
root = bst_insert(root, 'Eva', '321-123')
print("add Eva: ", root)
print("====== END TEST =======\n\n\n")
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
elif name<root['name']:
return bst_find(root['left'], name)
else:
return bst_find(root['right'], name)
print("====== START FIND TEST ======")
print("search by Ivan`s phone: ", bst_find(root, 'Ivan'))
print("search by Eva`s phone: ", bst_find(root,'Eva'))
print("====== END TEST ====== \n\n\n")
def find_min(node):
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root,name):
if root is None:
return None
if name< root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
min_node = find_min(root['right'])
root['name'] = min_node['name']
root['phone'] = min_node['phone']
root['right'] = bst_delete(root['right'], min_node['name'])
return root
def bst_list_all(root):
result = []
def inorder(node):
if node is None:
return
inorder(node['left'])
result.append((node['name'], node['phone']))
inorder(node['right'])
inorder(root)
return result
print("====== GLOBAL TEST TREES ======")
root = None
root = bst_insert(root, "Ivan", "123-456")
print("add Ivan: ", root)
root = bst_insert(root, "Boris", "789-012")
print("add Boris: ", root)
root = bst_insert(root, "Anna", "345-678")
print("add Anna: ", root)
root = bst_insert(root, "Ivan", "111-222") # обновление
print("update Ivan: ", root)
print("Find Ivan`s phone: ",bst_find(root, "Ivan")) # 111-222
print("Find Peter`s phone: ",bst_find(root, "Petr")) # None
root = bst_delete(root, "Boris")
print("Del Boris")
print("Find Boris: ",bst_find(root, "Boris")) # None
print("Find ALL: ",bst_list_all(root)) # [('Anna','345-678'), ('Ivan','111-222')]
print("====== END TEST ======")
# ======================== EXPEREMENT CHAPTER ========================
import random
import time
import csv
import sys
sys.setrecursionlimit(20000)
def generate_records(n, seed=42):
random.seed(seed)
records = []
for i in range(1, n+1):
name = f"User_{i:05d}"
phone = f"{random.randint(100,999)}-{random.randint(1000,9999)}"
records.append((name, phone))
return records
def prepare_datasets(base_records):
shuffled = base_records.copy()
random.shuffle(shuffled)
sorted_records = sorted(base_records, key=lambda x: x[0])
return shuffled, sorted_records
def run_experiment(struct_funcs, records, mode_name, repeats=5):
results = []
for rep in range(repeats):
struct = struct_funcs['create']()
# enter all records
start = time.perf_counter()
for name, phone in records:
struct = struct_funcs['insert'](struct, name, phone)
end = time.perf_counter()
insert_time = end - start
# search for 110 records (100 real + 10 None)
existing_names = [name for name, _ in records]
sample_existing = random.sample(existing_names, 100)
nonexistent = [f"None_{i}" for i in range(10)]
search_names = sample_existing + nonexistent
random.shuffle(search_names)
start = time.perf_counter()
for name in search_names:
_ = struct_funcs['find'](struct, name)
end = time.perf_counter()
find_time = end - start
# delete 10 random records
to_delete = random.sample(existing_names, 10)
start = time.perf_counter()
for name in to_delete:
struct = struct_funcs['delete'](struct, name)
end = time.perf_counter()
delete_time = end - start
results.append({
'structure': struct_funcs['name'],
'mode': mode_name,
'repetition': rep+1,
'insert_time': insert_time,
'find_time': find_time,
'delete_time': delete_time
})
return results
def main():
N = 1000
base_records = generate_records(N)
shuffled, sorted_records = prepare_datasets(base_records)
structures = {
'LinkedList': {
'name': 'LinkedList',
'create': lambda: None,
'insert': ll_insert,
'find': ll_find,
'delete': ll_delete,
'list_all': ll_list_all
},
'HashTable': {
'name': 'HashTable',
'create': lambda: [None] * 10,
'insert': ht_insert,
'find': ht_find,
'delete': ht_delete,
'list_all': ht_list_all
},
'BST': {
'name': 'BST',
'create': lambda: None,
'insert': bst_insert,
'find': bst_find,
'delete': bst_delete,
'list_all': bst_list_all
}
}
all_results = []
repeats = 5
for struct_name, funcs in structures.items():
print(f"Testing {struct_name} on random order...")
res = run_experiment(funcs, shuffled, 'random', repeats)
all_results.extend(res)
print(f"Testing {struct_name} in sorted order...")
res = run_experiment(funcs, sorted_records, 'sorted', repeats)
all_results.extend(res)
with open('experiment_results.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Structure', 'Mode', 'Repeat', 'Insert (sec)', 'Search (sec)', 'Delete (sec)'])
for r in all_results:
writer.writerow([
r['structure'],
r['mode'],
r['repetition'],
f"{r['insert_time']:.6f}",
f"{r['find_time']:.6f}",
f"{r['delete_time']:.6f}"
])
print("The experiment is complete. The results are saved in experiment_results.csv.")
if __name__ == '__main__':
main()

View File

@ -0,0 +1,31 @@
Structure,Mode,Repeat,Insert (sec),Search (sec),Delete (sec)
LinkedList,random,1,0.140358,0.007040,0.000844
LinkedList,random,2,0.138009,0.009197,0.000413
LinkedList,random,3,0.114717,0.009266,0.000744
LinkedList,random,4,0.117224,0.006914,0.000531
LinkedList,random,5,0.136302,0.010432,0.000582
LinkedList,sorted,1,0.106921,0.007845,0.000566
LinkedList,sorted,2,0.116404,0.015005,0.004900
LinkedList,sorted,3,0.125122,0.006956,0.000708
LinkedList,sorted,4,0.122401,0.004220,0.000474
LinkedList,sorted,5,0.111422,0.008343,0.000551
HashTable,random,1,0.025442,0.004652,0.000078
HashTable,random,2,0.035477,0.000985,0.000091
HashTable,random,3,0.015387,0.001249,0.000298
HashTable,random,4,0.014196,0.001167,0.000096
HashTable,random,5,0.013819,0.000910,0.000094
HashTable,sorted,1,0.013713,0.000897,0.000060
HashTable,sorted,2,0.016816,0.001013,0.000116
HashTable,sorted,3,0.018408,0.001019,0.000084
HashTable,sorted,4,0.014490,0.000886,0.000093
HashTable,sorted,5,0.012493,0.000867,0.000075
BST,random,1,0.006755,0.000468,0.000065
BST,random,2,0.006454,0.000380,0.000052
BST,random,3,0.003348,0.000266,0.000033
BST,random,4,0.004785,0.000379,0.000053
BST,random,5,0.005253,0.000438,0.000083
BST,sorted,1,0.331066,0.028260,0.002915
BST,sorted,2,0.342009,0.025769,0.003155
BST,sorted,3,0.282425,0.031293,0.002984
BST,sorted,4,0.313816,0.022712,0.002957
BST,sorted,5,0.287008,0.032645,0.002415
1 Structure Mode Repeat Insert (sec) Search (sec) Delete (sec)
2 LinkedList random 1 0.140358 0.007040 0.000844
3 LinkedList random 2 0.138009 0.009197 0.000413
4 LinkedList random 3 0.114717 0.009266 0.000744
5 LinkedList random 4 0.117224 0.006914 0.000531
6 LinkedList random 5 0.136302 0.010432 0.000582
7 LinkedList sorted 1 0.106921 0.007845 0.000566
8 LinkedList sorted 2 0.116404 0.015005 0.004900
9 LinkedList sorted 3 0.125122 0.006956 0.000708
10 LinkedList sorted 4 0.122401 0.004220 0.000474
11 LinkedList sorted 5 0.111422 0.008343 0.000551
12 HashTable random 1 0.025442 0.004652 0.000078
13 HashTable random 2 0.035477 0.000985 0.000091
14 HashTable random 3 0.015387 0.001249 0.000298
15 HashTable random 4 0.014196 0.001167 0.000096
16 HashTable random 5 0.013819 0.000910 0.000094
17 HashTable sorted 1 0.013713 0.000897 0.000060
18 HashTable sorted 2 0.016816 0.001013 0.000116
19 HashTable sorted 3 0.018408 0.001019 0.000084
20 HashTable sorted 4 0.014490 0.000886 0.000093
21 HashTable sorted 5 0.012493 0.000867 0.000075
22 BST random 1 0.006755 0.000468 0.000065
23 BST random 2 0.006454 0.000380 0.000052
24 BST random 3 0.003348 0.000266 0.000033
25 BST random 4 0.004785 0.000379 0.000053
26 BST random 5 0.005253 0.000438 0.000083
27 BST sorted 1 0.331066 0.028260 0.002915
28 BST sorted 2 0.342009 0.025769 0.003155
29 BST sorted 3 0.282425 0.031293 0.002984
30 BST sorted 4 0.313816 0.022712 0.002957
31 BST sorted 5 0.287008 0.032645 0.002415

View File

@ -0,0 +1,44 @@
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# Загрузка данных
df = pd.read_csv('experiment_results.csv')
# Усреднение по повторам
mean_times = df.groupby(['Structure', 'Mode'])[['Insert (sec)', 'Search (sec)', 'Delete (sec)']].mean().reset_index()
# Подготовка данных для графиков
structures = mean_times['Structure'].unique()
modes = mean_times['Mode'].unique()
# Создание трех графиков (вставка, поиск, удаление)
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
operations = ['Insert (sec)', 'Search (sec)', 'Delete (sec)']
titles = ['Вставка', 'Поиск', 'Удаление']
for ax, op, title in zip(axes, operations, titles):
# Для каждой структуры строим две колонки (random, sorted)
x = np.arange(len(structures))
width = 0.35
random_vals = []
sorted_vals = []
for s in structures:
random_row = mean_times[(mean_times['Structure']==s) & (mean_times['Mode']=='random')]
sorted_row = mean_times[(mean_times['Structure']==s) & (mean_times['Mode']=='sorted')]
random_vals.append(random_row[op].values[0] if not random_row.empty else 0)
sorted_vals.append(sorted_row[op].values[0] if not sorted_row.empty else 0)
ax.bar(x - width/2, random_vals, width, label='Случайный')
ax.bar(x + width/2, sorted_vals, width, label='Отсортированный')
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.set_ylabel('Время (сек)')
ax.set_title(title)
ax.legend()
plt.tight_layout()
plt.savefig('../../performance_comparison.png', dpi=150)
plt.show()

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,60 @@
# Отчёт по лабораторной работе "Структуры данных"
## 1. Введение
В рамках работы были реализованы три структуры данных для хранения телефонного справочника: связный список, хеш-таблица и двоичное дерево поиска. Проведено экспериментальное сравнение производительности операций вставки, поиска и удаления на наборе из **10000 записей**. Для каждой структуры тестирование выполнялось на двух вариантах входных данных: случайный порядок и отсортированный по имени. Каждый эксперимент повторялся 5 раз, результаты усреднены.
## 2. Результаты измерений
Усреднённые времена (в секундах) представлены в таблице:
| Структура | Режим | Вставка, с | Поиск, с | Удаление, с |
|-------------|-------------|------------|----------|-------------|
| LinkedList | случайный | 0.1143 | 0.0078 | 0.00065 |
| LinkedList | сортир. | 0.1124 | 0.0068 | 0.00065 |
| HashTable | случайный | 0.0131 | 0.00109 | 0.000085 |
| HashTable | сортир. | 0.0156 | 0.00110 | 0.00014 |
| BST | случайный | 0.00532 | 0.000365 | 0.000053 |
| BST | сортир. | 0.303 | 0.0230 | 0.00268 |
Графическое представление результатов приведено на рисунке ниже.
![Сравнение производительности](performance_comparison.png)
## 3. Анализ результатов
### 3.1. Влияние порядка данных на BST
При вставке элементов в отсортированном порядке двоичное дерево поиска вырождается в линейный список все новые узлы добавляются только в правое поддерево. Высота дерева становится равной количеству элементов, и сложность всех операций возрастает до **O(n)**. Эксперимент подтверждает это:
- Вставка в BST на отсортированных данных заняла **0.303 с**, что в **57 раз** больше, чем на случайных (0.00532 с).
- Время вставки на отсортированных данных даже превышает показатели связного списка (0.112 с), что объясняется дополнительными накладными расходами на рекурсивные вызовы.
- Поиск и удаление также замедлились примерно в 60 раз по сравнению со случайным режимом.
### 3.2. Устойчивость хеш-таблицы к порядку
Хеш-таблица использует хеш-функцию, которая равномерно распределяет ключи по корзинам независимо от порядка поступления. Поэтому производительность операций практически не зависит от того, в каком порядке приходят данные:
- В случайном и отсортированном режимах времена вставки (0.0131 и 0.0156 с) и поиска (около 0.0011 с) близки.
- Небольшие колебания могут быть вызваны случайным распределением коллизий.
- Это соответствует ожидаемой средней сложности **O(1)**.
### 3.3. Медлительность связного списка при поиске
Связный список не обеспечивает прямого доступа к элементам для поиска необходимо просматривать узлы последовательно, что даёт сложность **O(n)**. В эксперименте:
- Время поиска в списке (~0.007 с) на порядок больше, чем в хеш-таблице (0.0011 с) и BST на случайных данных (0.00037 с).
- При увеличении объёма данных эта разница будет только расти.
- Вставка в список также относительно медленна (0.11 с), так как требует прохода до конца (хотя обновление существующего имени выполняется быстрее, но в тесте все имена уникальны, поэтому каждая вставка проходит весь список).
### 3.4. Сравнение удаления
- **Связный список**: удаление требует сначала найти элемент (O(n)), затем переставить ссылки (O(1)). Время удаления (0.00065 с) близко ко времени поиска, что логично.
- **Хеш-таблица**: удаление выполняется за O(1) в среднем сначала определяется корзина, затем из короткого списка удаляется элемент. Время удаления (0.0000850.00014 с) значительно меньше, чем в списке.
- **BST**: на случайных данных удаление очень быстрое (0.000053 с) благодаря логарифмической высоте. На отсортированных данных время возрастает до 0.00268 с (в 50 раз), что отражает деградацию до O(n).
## 4. Выводы и рекомендации по выбору структуры
На основе полученных результатов можно сформулировать следующие рекомендации:
- **Хеш-таблица** оптимальный выбор, если требуется максимальная скорость поиска, вставки и удаления, а порядок хранения не важен. Примеры: реализация словарей, кэшей, индексов по ключу. В эксперименте хеш-таблица показала стабильно высокую производительность во всех режимах.
- **Двоичное дерево поиска** следует применять, когда необходимо получать данные в отсортированном порядке (например, вывод телефонного справочника по алфавиту). Однако важно учитывать, что при поступлении отсортированных данных дерево вырождается, и производительность резко падает. В таких случаях лучше использовать сбалансированные деревья (AVL, красно-чёрные). В эксперименте BST на случайных данных показал отличные результаты, близкие к хеш-таблице, а на отсортированных стал самым медленным.
- **Связный список** практически непригоден для больших объёмов данных из-за линейной сложности основных операций. Может использоваться лишь для очень маленьких коллекций, при частых вставках в начало списка (здесь не рассматривалось) или в учебных целях.
Таким образом, для реальных задач чаще всего выбирают хеш-таблицы или сбалансированные деревья в зависимости от требований к упорядоченности данных.
I use arch BTW

1
Ezhovnd/425.md Normal file
View File

@ -0,0 +1 @@
hi

0
GorkinMM/425.md Normal file
View File

0
GutovVM/428b.md Normal file
View File

0
KislyuninED/428.md Normal file
View File

0
KolbasovPD/425.md Normal file
View File

0
KorotkinSE/428b.md Normal file
View File

0
KuznetsovMA/429.txt Normal file
View File

1
KuznetsovYuM/428.md Normal file
View File

@ -0,0 +1 @@
428

0
LarikovaAA/428b.md Normal file
View File

0
LukovnikovDE/428.md Normal file
View File

0
MarkinAM/428b.md Normal file
View File

0
MochalovAE/426.txt Normal file
View File

330
README.md
View File

@ -16,7 +16,7 @@
### Крайний срок приема работ 25.05.2026 до 14:00 ### Крайний срок приема работ 25.05.2026 до 14:00
## Задание 1 -- репозиторий [отдельный срок на создание PR с папкой: 28.02.2026] ## Задание 0 -- репозиторий [отдельный срок на создание PR с папкой: 28.02.2026]
0. Создай пользователя (логин — фамилия+инициалы слитно транслитом, как в терминал-классе). 0. Создай пользователя (логин — фамилия+инициалы слитно транслитом, как в терминал-классе).
@ -53,4 +53,330 @@
- Базовая ветка: **develop** - Базовая ветка: **develop**
- Сравниваемая ветка: **свой форк / IvanovII** - Сравниваемая ветка: **свой форк / IvanovII**
8. Отправь PR. 8. Отправь PR.
## Задание 1 -- структуры данных
***Напоминание: под каждое задание вы создаете отдельную ветку***
>Для оформления результатов заведи папку **docs** в своей папке и сохраняй туда отчет (в любом формате от .doc до .md, а то и .jpnb). Вспомогательные файлы клади в подпапку **data** внутри **docs**
**Цель работы**
Реализовать три различные структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций. Вы должны собственными руками написать код, чтобы понять внутреннее устройство связного списка, хеш-таблицы и двоичного дерева поиска, а также осознать их сильные и слабые стороны на практике.
**!! Задание выполнять в структурной (процедурной) парадигме, не используя классы. Главное реализовать структуры данных «руками» и сравнить их производительность.**
### Базовые операции (обязательны для всех):
`insert(name, phone)` -- добавить или обновить запись.
`find(name)` -- phone или None.
`delete(name)` -- удалить запись, игнорировать отсутствие.
`list_all()` -- список всех записей, отсортированный по имени (для BST inorder обход; для списка и хеш‑таблицы — собрать и отсортировать явно).
#### 1. Связный список (LinkedListPhoneBook)
Узел представляется словарём: `{'name': 'Имя', 'phone': '123', 'next': None}.`
**Функции:**
`def ll_insert(head, name, phone)` — проходит до конца (или сразу добавляет в конец) и возвращает новую голову (если вставка в начало) или изменяет список по ссылке. Удобнее возвращать новую голову, если вставка может быть в начало.
`def ll_find(head, name)` — ищет узел, возвращает телефон или None.
`def ll_delete(head, name)` — удаляет узел, возвращает новую голову.
`def ll_list_all(head)` — собирает все записи в список и сортирует (сортировка вынесена отдельно).
#### 2. Хеш-таблица
Хранится как список buckets фиксированной длины, каждый элемент — голова связного списка (или None).
**Функции:**
`def ht_insert(buckets, name, phone)` — вычисляет индекс, вызывает ll_insert для соответствующего бакета.
Аналогично `ht_find, ht_delete, ht_list_all` (последняя собирает все записи из всех бакетов и сортирует).
#### 3. Двоичное дерево поиска
Узел — словарь: `{'name': 'Имя', 'phone': '123', 'left': None, 'right': None}.`
**Функции:**
`def bst_insert(root, name, phone)` — рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется).
`def bst_find(root, name)` — поиск.
`def bst_delete(root, name)` — удаление, возвращает новый корень.
`def bst_list_all(root)` — центрированный обход (рекурсивно собирает записи в отсортированном порядке).
### Экспериментальная часть (подробно об измерении времени)
#### 1. Генерация тестовых данных
Создайте список records из N элементов (например, N = 10000). Каждый элемент — кортеж (name, phone).
Имена генерируйте как `f"User_{i:05d}"` (равномерное распределение) или случайные слова из небольшого набора (чтобы были повторения и коллизии). Для проверки влияния порядка подготовьте два варианта одного и того же набора:
`records_shuffled` — случайный порядок.
`records_sorted` — отсортированный по имени (по алфавиту).
#### 2. Инструменты замера времени
Используйте модуль **time**:
```python
import time
start = time.perf_counter()
# ... операции ...
end = time.perf_counter()
elapsed = end - start # время в секундах
```
Для многократных замеров удобен `timeit`, но в этой задаче достаточно просто обернуть код в цикл и усреднить.
#### 3. Проведение замеров
Для каждой структуры данных и для каждого режима входных данных (случайный / отсортированный) выполните:
- А. Вставка всех записей
Создайте пустую структуру.
Засеките время, выполните insert для каждой записи из входного списка.
Зафиксируйте общее время вставки.
- Б. Поиск 100 случайных записей
Возьмите 100 случайных имён из того же набора (гарантированно существующих) и 10 имён, которых нет (например, "None_{i}").
Засеките время на выполнение всех 110 вызовов find.
- В. Удаление 50 случайных записей
Выберите 50 случайных имён из набора.
Засеките время на выполнение delete для каждого.
**!! Важно: после вставки структура остаётся заполненной, поиск и удаление выполняются на ней же. Если нужно повторить замер для другого порядка данных — создавайте новую структуру и заполняйте заново.**
#### 4. Сохранение результатов
**!! Каждый эксперимент повторить минимум 5 раз и записывать и среднее время, и все замеры.**
Соберите все замеры в словарь или список, затем сохраните в CSV-файл:
```python
import csv
results = [
["Структура", "Режим", "Операция", "Время (сек)"],
["LinkedList", "случайный", "вставка", 0.123],
...
]
with open("results.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(results)
```
#### 5. Анализ результатов
Постройте график (столбчатая диаграмма или линейный график) — можно в Excel, Google Sheets или с помощью matplotlib в Python.
Сравните:
- Как порядок входных данных влияет на скорость вставки в BST (деградация до O(n) на отсортированных данных).
- Почему хеш-таблица почти не чувствительна к порядку.
- Почему связный список всегда медленен при поиске.
- Как удаление работает в каждой структуре.
* Вывод должен содержать ответ на вопрос: какую структуру и для каких задач (частые вставки, частый поиск, необходимость получать данные в порядке) стоит выбирать в реальной жизни.*
## Задание: Поиск выхода из лабиринта (объектно-ориентированная реализация с паттернами)
### Цель работы
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 3 паттерна проектирования из списка GoF, обосновать их выбор и продемонстрировать преимущества такой архитектуры.
### Общая схема приложения (пример)
```mermaid
classDiagram
class Maze {
-Cell[] cells
-int width, height
-Cell start
-Cell exit
+getCell(x,y): Cell
+getNeighbors(cell): List~Cell~
}
class Cell {
-int x, y
-bool isWall
-bool isStart
-bool isExit
+isPassable(): bool
}
class MazeBuilder {
<<interface>>
+buildFromFile(filename): Maze
}
class TextFileMazeBuilder {
+buildFromFile(filename): Maze
}
class PathFindingStrategy {
<<interface>>
+findPath(maze, start, exit): List~Cell~
}
class BFSStrategy
class DFSStrategy
class AStarStrategy
class DijkstraStrategy
class SearchStats {
+timeMs: float
+visitedCells: int
+pathLength: int
}
class MazeSolver {
-Maze maze
-PathFindingStrategy strategy
+setStrategy(strategy)
+solve(): SearchStats
}
class Command {
<<interface>>
+execute()
+undo()
}
class MoveCommand {
-Player player
-Direction dir
-Cell previousCell
+execute()
+undo()
}
class Player {
-Cell currentCell
+moveTo(cell)
}
class Observer {
<<interface>>
+update(event)
}
class ConsoleView {
+update(event)
+render(maze, player, path)
}
MazeBuilder <|.. TextFileMazeBuilder
MazeBuilder --> Maze : creates
PathFindingStrategy <|.. BFSStrategy
PathFindingStrategy <|.. DFSStrategy
PathFindingStrategy <|.. AStarStrategy
PathFindingStrategy <|.. DijkstraStrategy
MazeSolver --> PathFindingStrategy : uses
MazeSolver --> Maze : uses
Command <|.. MoveCommand
MoveCommand --> Player
Player --> Cell
Observer <|.. ConsoleView
MazeSolver --> Observer : notifies
```
### Выполнение
#### Этап 1. Модель лабиринта (без паттернов, просто классы)
**Задача:** Создать классы `Cell` и `Maze`, которые представляют карту лабиринта.
- `Cell` хранит координаты (x, y), флаги `isWall`, `isStart`, `isExit`, метод `isPassable()` (возвращает `True` для прохода, если не стена).
- `Maze` хранит двумерный массив клеток, ширину, высоту, ссылки на стартовую и выходную клетку. Методы: `getCell(x, y)`, `getNeighbors(cell)` возвращает список соседних проходимых клеток (вверх, вниз, влево, вправо, если в пределах границ и не стена).
**Результат:** Лабиринт можно создать вручную в коде, но загрузку пока не делаем.
#### Этап 2. Загрузка лабиринта из файла применение паттерна **Builder**
**Задача:** Реализовать загрузку лабиринта из текстового файла, где `#` стена, ` ` (пробел) проход, `S` старт, `E` выход.
- Создать интерфейс `MazeBuilder` с методом `buildFromFile(filename)`.
- Реализовать класс `TextFileMazeBuilder`, который читает файл, парсит символы, создаёт объекты `Cell`, задаёт координаты и флаги, после чего возвращает готовый `Maze`.
Процесс построения лабиринта сложный (парсинг, валидация, установка старта/выхода). Builder скрывает детали создания от клиента. В будущем можно легко добавить другой формат (например, JSON или бинарный) через новую реализацию `MazeBuilder`.
#### Этап 3. Стратегии поиска пути паттерн **Strategy**
**Задача:** Реализовать семейство алгоритмов поиска пути от старта до выхода.
- Создать интерфейс `PathFindingStrategy` с методом `findPath(maze, start, exit)`, возвращающим список клеток пути (от старта до выхода включительно) или пустой список, если пути нет.
- Реализовать минимум 3 стратегии:
- **BFS** (поиск в ширину) гарантирует кратчайший путь по количеству шагов.
- **DFS** (поиск в глубину) быстрый, но не обязательно кратчайший.
- **A*** (с эвристикой, например, манхэттенское расстояние) компромисс между скоростью и оптимальностью.
- (Опционально) **Дейкстра** полезна для взвешенных лабиринтов, но в базовом варианте все шаги имеют вес 1, тогда она совпадает с BFS.
Каждая стратегия возвращает путь. Для BFS/DFS используйте очередь/стек, для A* приоритетную очередь (heapq). Важно: алгоритмы не должны модифицировать сам лабиринт, только читать состояние клеток.
Strategy позволяет легко переключать алгоритмы во время выполнения, не меняя код остальной программы. Новый алгоритм можно добавить, реализовав интерфейс.
#### Этап 4. Класс-оркестратор **MazeSolver** (использует Strategy)
**Задача:** Создать класс, который принимает лабиринт и стратегию, выполняет поиск и собирает статистику.
- `MazeSolver` содержит поля `maze` и `strategy`.
- Метод `setStrategy(strategy)` для динамической смены алгоритма.
- Метод `solve()` вызывает `strategy.findPath(...)` и возвращает объект `SearchStats` (время выполнения в миллисекундах, количество посещённых клеток, длина найденного пути).
- Для замера времени используйте `time.perf_counter()` до и после вызова стратегии.
#### Этап 5. Визуализация и пошаговое управление паттерны **Observer** и **Command** (по желанию)
**5.1. Наблюдатель (Observer)** обновление консольного интерфейса.
- Создать интерфейс `Observer` с методом `update(event)`, где `event` может быть строкой или объектом с типом события (`"path_found"`, `"move"`, `"maze_loaded"`).
- Реализовать класс `ConsoleView`, который отображает лабиринт, текущее положение игрока (если реализован пошаговый режим) и найденный путь. Метод `render(maze, player_position, path)` рисует карту в консоли.
- `MazeSolver` (или отдельный контроллер) может иметь список наблюдателей и уведомлять их при изменении состояния.
**5.2. Команда (Command)** для пошагового перемещения игрока по найденному пути (или ручного управления).
- Создать интерфейс `Command` с методами `execute()` и `undo()`.
- Реализовать `MoveCommand`, который принимает игрока (`Player`), направление и изменяет его позицию, сохраняя предыдущую для отмены.
- Создать класс `Player`, хранящий текущую клетку.
- Консольное меню позволяет вводить команды (W/A/S/D), выполнять `MoveCommand`, при необходимости отменять последний ход (Ctrl+Z). Это опционально, но очень наглядно демонстрирует паттерн.
*Observer можно реализовать только для вывода сообщений о начале/конце поиска, а Command для демонстрации undo при ручном исследовании лабиринта.*
#### Этап 6. Экспериментальная часть (аналогично заданию со структурами данных)
**Задача:** Сравнить эффективность реализованных стратегий на лабиринтах разной сложности.
1. **Подготовка тестовых лабиринтов:**
- Маленький (10×10) с простым путём.
- Средний (50×50) с тупиками.
- Большой (100×100) с запутанной структурой.
- «Пустой» лабиринт (без стен) для демонстрации максимальной производительности.
- «Без выхода» чтобы проверить обработку отсутствия пути.
2. **Замеры:**
- Для каждого лабиринта и каждой стратегии запустить `solve()` 510 раз, усреднить время, количество посещённых клеток, длину пути.
- Записать результаты в CSV: `лабиринт,стратегия,время_мс,посещено_клеток,длина_пути`.
3. **Анализ:**
- Построить графики для каждого лабиринта.
- Проанализировать и написать выводы по итогам (эффективность того или иного алгоритма в разных случаях).
4. **Дополнительное задание:** Реализовать взвешенные клетки (например, болото вес 3, песок вес 2, асфальт вес 1) и сравнить Дейкстру с A* на взвешенном графе.
#### Этап 7. Отчёт
**Структура отчёта:**
1. Описание задачи и выбранных паттернов (с диаграммой классов из Mermaid).
2. Листинги ключевых классов (можно выборочно) или ссылка на репозиторий.
3. Результаты экспериментов (таблицы, графики).
4. Анализ эффективности алгоритмов и применимости паттернов.
5. Выводы: как ООП и паттерны помогли сделать код гибким и расширяемым. Что было бы сложно изменить без них.
### Советы
- Для A* самая простая эвристика: `abs(x1 - x2) + abs(y1 - y2)`.
- При поиске пути надо хранить предшественников (`parent` для каждой посещённой клетки), чтобы восстановить путь.
- Для BFS/DFS используй `deque` (очередь) и `list` (стек).
- Визуализацию в консоли можно сделать с помощью `os.system('cls' if os.name == 'nt' else 'clear')` для перерисовки.

0
SavelevMI/428.md Normal file
View File

0
ShulpinIN/428.md Normal file
View File

0
SobolevNS/426 Normal file
View File

0
SokolovEN/426 Normal file
View File

0
SokolovNE/428b.md.txt Normal file
View File

0
SolovevDD/425.md Normal file
View File

1
SorokinAD/428.md Normal file
View File

@ -0,0 +1 @@
1

0
VarnakovAA/429.md Normal file
View File

0
VildyaevAV/426 Normal file
View File

0
ZhuravlevDV/425.txt Normal file
View File

0
agafonovdm/425.txt Normal file
View File

0
anikinvd/428.md Normal file
View File

0
duznb/429.md.txt Normal file
View File

0
famutdinovmd/428b.md Normal file
View File

0
fomichevks/426.md.txt Normal file
View File

0
groshevava/426.md.txt Normal file
View File

0
kornevma/426.md Normal file
View File

1
kuznetsovTD/428b.md Normal file
View File

@ -0,0 +1 @@
428b.md

0
meosyam/428.md.txt Normal file
View File

1
nehoroshevaa/428b.md Normal file
View File

@ -0,0 +1 @@
428b

0
nikitovie/425.txt Normal file
View File

0
novikovsd/428 Normal file
View File

6
osipovamd/428.md Normal file
View File

@ -0,0 +1,6 @@
{\rtf1\ansi\ansicpg1251\cocoartf2869
\cocoatextscaling0\cocoaplatform0{\fonttbl}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
}

0
petryaninyas/426.md Normal file
View File

0
pogodinda/427.md.txt Normal file
View File

BIN
raskatovia/429.md Normal file

Binary file not shown.

1
rybakovaa/428b.md Normal file
View File

@ -0,0 +1 @@
428b

0
semyanovra/426.md Normal file
View File

View File

@ -0,0 +1,31 @@
Structure,Mode,Repeat,Insert (sec),Search (sec),Delete (sec)
LinkedList,random,1,3.972341,0.027657,0.012911
LinkedList,random,2,4.045646,0.023430,0.015166
LinkedList,random,3,4.108713,0.029786,0.011930
LinkedList,random,4,4.177241,0.028833,0.014464
LinkedList,random,5,4.185596,0.029333,0.012727
LinkedList,sorted,1,3.790176,0.025204,0.010269
LinkedList,sorted,2,3.810435,0.022951,0.011524
LinkedList,sorted,3,3.803720,0.025208,0.010396
LinkedList,sorted,4,3.815409,0.027041,0.010837
LinkedList,sorted,5,3.803349,0.025340,0.011777
HashTable,random,1,0.010245,0.000075,0.000036
HashTable,random,2,0.008733,0.000079,0.000069
HashTable,random,3,0.013354,0.000094,0.000044
HashTable,random,4,0.008903,0.000078,0.000036
HashTable,random,5,0.009199,0.000072,0.000033
HashTable,sorted,1,0.010286,0.000114,0.000052
HashTable,sorted,2,0.009219,0.000073,0.000034
HashTable,sorted,3,0.011302,0.000068,0.000033
HashTable,sorted,4,0.009324,0.000068,0.000033
HashTable,sorted,5,0.008641,0.000068,0.000034
BST,random,1,0.027580,0.000190,0.000118
BST,random,2,0.020693,0.000188,0.000116
BST,random,3,0.020889,0.000190,0.000109
BST,random,4,0.022945,0.000182,0.000110
BST,random,5,0.022395,0.000207,0.000114
BST,sorted,1,9.109235,0.083432,0.049594
BST,sorted,2,9.177649,0.097374,0.050929
BST,sorted,3,9.414714,0.067665,0.054041
BST,sorted,4,9.062772,0.090823,0.048369
BST,sorted,5,8.994138,0.072883,0.049921
1 Structure Mode Repeat Insert (sec) Search (sec) Delete (sec)
2 LinkedList random 1 3.972341 0.027657 0.012911
3 LinkedList random 2 4.045646 0.023430 0.015166
4 LinkedList random 3 4.108713 0.029786 0.011930
5 LinkedList random 4 4.177241 0.028833 0.014464
6 LinkedList random 5 4.185596 0.029333 0.012727
7 LinkedList sorted 1 3.790176 0.025204 0.010269
8 LinkedList sorted 2 3.810435 0.022951 0.011524
9 LinkedList sorted 3 3.803720 0.025208 0.010396
10 LinkedList sorted 4 3.815409 0.027041 0.010837
11 LinkedList sorted 5 3.803349 0.025340 0.011777
12 HashTable random 1 0.010245 0.000075 0.000036
13 HashTable random 2 0.008733 0.000079 0.000069
14 HashTable random 3 0.013354 0.000094 0.000044
15 HashTable random 4 0.008903 0.000078 0.000036
16 HashTable random 5 0.009199 0.000072 0.000033
17 HashTable sorted 1 0.010286 0.000114 0.000052
18 HashTable sorted 2 0.009219 0.000073 0.000034
19 HashTable sorted 3 0.011302 0.000068 0.000033
20 HashTable sorted 4 0.009324 0.000068 0.000033
21 HashTable sorted 5 0.008641 0.000068 0.000034
22 BST random 1 0.027580 0.000190 0.000118
23 BST random 2 0.020693 0.000188 0.000116
24 BST random 3 0.020889 0.000190 0.000109
25 BST random 4 0.022945 0.000182 0.000110
26 BST random 5 0.022395 0.000207 0.000114
27 BST sorted 1 9.109235 0.083432 0.049594
28 BST sorted 2 9.177649 0.097374 0.050929
29 BST sorted 3 9.414714 0.067665 0.054041
30 BST sorted 4 9.062772 0.090823 0.048369
31 BST sorted 5 8.994138 0.072883 0.049921

View File

@ -0,0 +1,303 @@
import random
import time
import csv
import sys
import pandas as pd
import matplotlib.pyplot as plt
sys.setrecursionlimit(20000)
def ll_insert(head, name, phone):
current = head
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
current = current['next']
new_node = {'name': name, 'phone': phone, 'next': None}
if head is None:
return new_node
current = head
while current['next'] is not None:
current = current['next']
current['next'] = new_node
return head
def ll_find(head, name):
current = head
while current is not None:
if current['name'] == name:
return current['phone']
current = current['next']
return None
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
current = head['next']
while current is not None:
if current['name'] == name:
prev['next'] = current['next']
return head
prev = current
current = current['next']
return head
def ll_list_all(head):
records = []
current = head
while current is not None:
records.append((current['name'], current['phone']))
current = current['next']
records.sort(key=lambda x: x[0])
return records
HASH_SIZE = 997
def hash_func(name, size):
return hash(name) % size
def ht_create():
return [None] * HASH_SIZE
def ht_insert(table, name, phone):
idx = hash_func(name, len(table))
table[idx] = ll_insert(table[idx], name, phone)
return table
def ht_find(table, name):
idx = hash_func(name, len(table))
return ll_find(table[idx], name)
def ht_delete(table, name):
idx = hash_func(name, len(table))
table[idx] = ll_delete(table[idx], name)
return table
def ht_list_all(table):
all_records = []
for head in table:
current = head
while current is not None:
all_records.append((current['name'], current['phone']))
current = current['next']
all_records.sort(key=lambda x: x[0])
return all_records
def bst_create_node(name, phone):
return {'name': name, 'phone': phone, 'left': None, 'right': None}
def bst_insert(root, name, phone):
if root is None:
return bst_create_node(name, phone)
if name == root['name']:
root['phone'] = phone
elif name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
else:
root['right'] = bst_insert(root['right'], name, phone)
return root
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
elif name < root['name']:
return bst_find(root['left'], name)
else:
return bst_find(root['right'], name)
def bst_find_min(node):
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root, name):
if root is None:
return None
if name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
min_node = bst_find_min(root['right'])
root['name'] = min_node['name']
root['phone'] = min_node['phone']
root['right'] = bst_delete(root['right'], min_node['name'])
return root
def bst_list_all(root):
result = []
def inorder(node):
if node is None:
return
inorder(node['left'])
result.append((node['name'], node['phone']))
inorder(node['right'])
inorder(root)
return result
def generate_records(num_records, seed=42):
random.seed(seed)
records = []
for i in range(1, num_records + 1):
name = f"User_{i:05d}"
phone = f"{random.randint(100,999)}-{random.randint(1000,9999)}"
records.append((name, phone))
return records
def prepare_datasets(base_records):
shuffled = base_records.copy()
random.shuffle(shuffled)
sorted_records = sorted(base_records, key=lambda x: x[0])
return shuffled, sorted_records
def run_experiment_for_structure(struct_funcs, records, mode_name, repeats=5):
results = []
for rep in range(repeats):
ds = struct_funcs['create']()
start = time.perf_counter()
for name, phone in records:
ds = struct_funcs['insert'](ds, name, phone)
insert_time = time.perf_counter() - start
existing_names = [rec[0] for rec in records]
sample_existing = random.sample(existing_names, 100)
nonexistent = [f"None_{i}" for i in range(10)]
search_names = sample_existing + nonexistent
random.shuffle(search_names)
start = time.perf_counter()
for name in search_names:
_ = struct_funcs['find'](ds, name)
find_time = time.perf_counter() - start
to_delete = random.sample(existing_names, 50)
start = time.perf_counter()
for name in to_delete:
ds = struct_funcs['delete'](ds, name)
delete_time = time.perf_counter() - start
results.append({
'structure': struct_funcs['name'],
'mode': mode_name,
'repetition': rep + 1,
'insert_time': insert_time,
'find_time': find_time,
'delete_time': delete_time
})
return results
def main_experiment():
N = 10000
REPEATS = 5
print("Генерация тестовых данных...")
base_records = generate_records(N)
shuffled_records, sorted_records = prepare_datasets(base_records)
print(f"Создано {N} записей. Случайный порядок и отсортированный готовы.")
structures = {
'LinkedList': {
'name': 'LinkedList',
'create': lambda: None,
'insert': ll_insert,
'find': ll_find,
'delete': ll_delete
},
'HashTable': {
'name': 'HashTable',
'create': ht_create,
'insert': ht_insert,
'find': ht_find,
'delete': ht_delete
},
'BST': {
'name': 'BST',
'create': lambda: None,
'insert': bst_insert,
'find': bst_find,
'delete': bst_delete
}
}
all_results = []
for struct_name, funcs in structures.items():
print(f"Тестирование {struct_name} на случайном порядке...")
all_results.extend(run_experiment_for_structure(funcs, shuffled_records, 'random', REPEATS))
print(f"Тестирование {struct_name} на отсортированном порядке...")
all_results.extend(run_experiment_for_structure(funcs, sorted_records, 'sorted', REPEATS))
csv_file = "experiment_results.csv"
with open(csv_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Structure', 'Mode', 'Repeat', 'Insert (sec)', 'Search (sec)', 'Delete (sec)'])
for rec in all_results:
writer.writerow([
rec['structure'],
rec['mode'],
rec['repetition'],
f"{rec['insert_time']:.6f}",
f"{rec['find_time']:.6f}",
f"{rec['delete_time']:.6f}"
])
print(f"Результаты сохранены в {csv_file}")
plot_results(csv_file)
def plot_results(csv_path):
df = pd.read_csv(csv_path)
mean_times = df.groupby(['Structure', 'Mode'])[['Insert (sec)', 'Search (sec)', 'Delete (sec)']].mean().reset_index()
structures = mean_times['Structure'].unique()
modes = mean_times['Mode'].unique()
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
operations = ['Insert (sec)', 'Search (sec)', 'Delete (sec)']
titles = ['Вставка', 'Поиск', 'Удаление']
for ax, op, title in zip(axes, operations, titles):
x = range(len(structures))
width = 0.35
random_vals = []
sorted_vals = []
for s in structures:
rand_row = mean_times[(mean_times['Structure'] == s) & (mean_times['Mode'] == 'random')]
sort_row = mean_times[(mean_times['Structure'] == s) & (mean_times['Mode'] == 'sorted')]
random_vals.append(rand_row[op].values[0] if not rand_row.empty else 0)
sorted_vals.append(sort_row[op].values[0] if not sort_row.empty else 0)
ax.bar([i - width/2 for i in x], random_vals, width, label='Случайный порядок')
ax.bar([i + width/2 for i in x], sorted_vals, width, label='Отсортированный порядок')
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.set_ylabel('Время (секунды)')
ax.set_title(title)
ax.legend()
plt.tight_layout()
plt.savefig('performance_comparison.png', dpi=150)
plt.show()
print("График сохранён как performance_comparison.png")
if __name__ == "__main__":
main_experiment()

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,629 @@
import sys
from collections import deque
import heapq
import time
import os
import csv
import matplotlib.pyplot as plt
import numpy as np
# ----------------------------- Модель клетки -----------------------------
class GridCell:
def __init__(self, x, y):
self._x = x
self._y = y
self._blocked = False
self._entry = False
self._exit_flag = False
@property
def x(self):
return self._x
@property
def y(self):
return self._y
@property
def is_wall(self):
return self._blocked
@is_wall.setter
def is_wall(self, value):
self._blocked = value
@property
def is_start(self):
return self._entry
@is_start.setter
def is_start(self, value):
self._entry = value
@property
def is_exit(self):
return self._exit_flag
@is_exit.setter
def is_exit(self, value):
self._exit_flag = value
def passable(self):
return not self._blocked
# ----------------------------- Модель лабиринта -----------------------------
class Labyrinth:
def __init__(self, width, height):
self._width = width
self._height = height
self._cells = [[GridCell(x, y) for x in range(width)] for y in range(height)]
self._start_cell = None
self._exit_cell = None
@property
def width(self):
return self._width
@property
def height(self):
return self._height
@property
def start(self):
return self._start_cell
@property
def exit(self):
return self._exit_cell
def cell_at(self, x, y):
if 0 <= x < self._width and 0 <= y < self._height:
return self._cells[y][x]
return None
def configure_cell(self, x, y, cell_type):
cell = self.cell_at(x, y)
if cell is None:
return
if cell_type == 'wall':
cell.is_wall = True
elif cell_type == 'start':
if self._start_cell:
self._start_cell.is_start = False
cell.is_start = True
cell.is_wall = False
self._start_cell = cell
elif cell_type == 'exit':
if self._exit_cell:
self._exit_cell.is_exit = False
cell.is_exit = True
cell.is_wall = False
self._exit_cell = cell
elif cell_type == 'path':
cell.is_wall = False
def adjacent_cells(self, cell):
neighbours = []
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]
for dx, dy in directions:
nx, ny = cell.x + dx, cell.y + dy
neighbour = self.cell_at(nx, ny)
if neighbour and neighbour.passable():
neighbours.append(neighbour)
return neighbours
# ----------------------------- Загрузка лабиринта -----------------------------
class LabyrinthBuilder:
def build_from_file(self, filename):
raise NotImplementedError
class TxtLabyrinthBuilder(LabyrinthBuilder):
def build_from_file(self, filename):
with open(filename, 'r') as f:
lines = [line.rstrip('\n') for line in f.readlines()]
height = len(lines)
width = max(len(line) for line in lines) if height > 0 else 0
start_cnt = 0
exit_cnt = 0
lab = Labyrinth(width, height)
for y, line in enumerate(lines):
for x, ch in enumerate(line):
if ch == "#":
lab.configure_cell(x, y, "wall")
elif ch == "S":
lab.configure_cell(x, y, "start")
start_cnt += 1
elif ch == "E":
lab.configure_cell(x, y, "exit")
exit_cnt += 1
else:
lab.configure_cell(x, y, 'path')
if start_cnt != 1 or exit_cnt != 1:
raise ValueError(f"Maze must have exactly one S and one E. Found S={start_cnt}, E={exit_cnt}")
return lab
# ----------------------------- Алгоритмы поиска -----------------------------
class SearchAlgorithm:
def compute_path(self, maze, start, goal):
raise NotImplementedError
def _build_path(self, came_from, start, goal):
path = []
cur = goal
while cur is not None:
path.append(cur)
cur = came_from.get(cur)
path.reverse()
return path
def visited_nodes(self):
return getattr(self, '_visited', 0)
class BFS(SearchAlgorithm):
def compute_path(self, maze, start, goal):
q = deque()
q.append(start)
came_from = {start: None}
visited = {start}
while q:
cur = q.popleft()
if cur == goal:
self._visited = len(visited)
return self._build_path(came_from, start, goal)
for nb in maze.adjacent_cells(cur):
if nb not in visited:
visited.add(nb)
came_from[nb] = cur
q.append(nb)
self._visited = len(visited)
return []
class DFS(SearchAlgorithm):
def compute_path(self, maze, start, goal):
stack = [start]
came_from = {start: None}
visited = {start}
while stack:
cur = stack.pop()
if cur == goal:
self._visited = len(visited)
return self._build_path(came_from, start, goal)
for nb in maze.adjacent_cells(cur):
if nb not in visited:
visited.add(nb)
came_from[nb] = cur
stack.append(nb)
self._visited = len(visited)
return []
class AStar(SearchAlgorithm):
def _heuristic(self, cell, goal):
return abs(cell.x - goal.x) + abs(cell.y - goal.y)
def compute_path(self, maze, start, goal):
heap = []
counter = 0
start_f = self._heuristic(start, goal)
heapq.heappush(heap, (start_f, counter, start))
counter += 1
came_from = {}
g_score = {start: 0}
f_score = {start: start_f}
visited = set()
while heap:
cur_f, _, cur = heapq.heappop(heap)
visited.add(cur)
if cur == goal:
self._visited = len(visited)
return self._build_path(came_from, start, goal)
if cur_f > f_score.get(cur, float('inf')):
continue
for nb in maze.adjacent_cells(cur):
tentative_g = g_score[cur] + 1
if tentative_g < g_score.get(nb, float('inf')):
came_from[nb] = cur
g_score[nb] = tentative_g
new_f = tentative_g + self._heuristic(nb, goal)
f_score[nb] = new_f
heapq.heappush(heap, (new_f, counter, nb))
counter += 1
self._visited = len(visited)
return []
# ----------------------------- Оркестратор -----------------------------
class Pathfinder:
def __init__(self, maze):
self._maze = maze
self._algorithm = None
self._listeners = []
def attach(self, listener):
self._listeners.append(listener)
def notify(self, event, data):
for lst in self._listeners:
lst.update(event, data)
def set_algorithm(self, algorithm):
self._algorithm = algorithm
def solve(self):
if self._algorithm is None:
return None
t0 = time.perf_counter()
path = self._algorithm.compute_path(self._maze, self._maze.start, self._maze.exit)
t1 = time.perf_counter()
elapsed_ms = (t1 - t0) * 1000
self.notify("path_found", path)
return PerformanceData(elapsed_ms, self._algorithm.visited_nodes(), len(path))
class PerformanceData:
def __init__(self, time_ms, visited, length):
self.time_ms = time_ms
self.visited_cells = visited
self.path_length = length
# ----------------------------- Наблюдатель и отображение -----------------------------
class EventListener:
def update(self, event_type, data):
raise NotImplementedError
class ConsoleDisplay(EventListener):
def __init__(self, walker=None):
self._last_path = None
self._walker = walker
def update(self, event_type, data):
if event_type == "maze_loaded":
self._render_maze(data)
elif event_type == "path_found":
self._last_path = data
self._render_path(data)
elif event_type == "player_moved":
self._render_maze_with_player(data)
def _render_maze(self, maze):
os.system('cls' if os.name == 'nt' else 'clear')
print("=" * (maze.width * 2 + 4))
print(" LABYRINTH")
print("=" * (maze.width * 2 + 4))
for y in range(maze.height):
print(" ", end='')
for x in range(maze.width):
cell = maze.cell_at(x, y)
if cell == maze.start:
print('S', end=' ')
elif cell == maze.exit:
print('E', end=' ')
elif cell.is_wall:
print('#', end=' ')
else:
print('.', end=' ')
print()
print("=" * (maze.width * 2 + 4))
print(" S - start E - exit # - wall . - path")
def _render_maze_with_player(self, maze):
os.system('cls' if os.name == 'nt' else 'clear')
print("=" * (maze.width * 2 + 4))
print(" LABYRINTH (P - player)")
print("=" * (maze.width * 2 + 4))
for y in range(maze.height):
print(" ", end='')
for x in range(maze.width):
cell = maze.cell_at(x, y)
if self._walker and cell == self._walker.current:
print('P', end=' ')
elif cell == maze.start:
print('S', end=' ')
elif cell == maze.exit:
print('E', end=' ')
elif cell.is_wall:
print('#', end=' ')
else:
print('.', end=' ')
print()
print("=" * (maze.width * 2 + 4))
print(f" Player position: ({self._walker.current.x}, {self._walker.current.y})")
print(" S - start E - exit # - wall . - path P - player")
def _render_path(self, path):
if not path:
print("\n Path not found!")
return
print(f"\n Path found! Length: {len(path)}")
# ----------------------------- Игрок и команды -----------------------------
class Walker:
def __init__(self, start_cell, lab):
self._current = start_cell
self._previous = None
self._labyrinth = lab
@property
def current(self):
return self._current
def move_to(self, cell):
if cell and cell.passable():
self._previous = self._current
self._current = cell
return True
return False
def undo_move(self):
if self._previous:
self._current, self._previous = self._previous, None
return True
return False
class Action:
def execute(self):
raise NotImplementedError
def undo(self):
raise NotImplementedError
class MoveAction(Action):
def __init__(self, walker, direction, lab):
self._walker = walker
self._dx, self._dy = direction
self._lab = lab
self._executed = False
def execute(self):
new_x = self._walker.current.x + self._dx
new_y = self._walker.current.y + self._dy
target = self._lab.cell_at(new_x, new_y)
if target and target.passable():
self._walker.move_to(target)
self._executed = True
return True
return False
def undo(self):
if self._executed:
self._walker.undo_move()
self._executed = False
return True
return False
# ----------------------------- Эксперименты и статистика -----------------------------
def run_benchmark(maze_file, algorithm, runs=5):
builder = TxtLabyrinthBuilder()
maze = builder.build_from_file(maze_file)
total_time = 0.0
total_visited = 0
total_length = 0
for _ in range(runs):
solver = Pathfinder(maze)
solver.set_algorithm(algorithm)
stats = solver.solve()
if stats:
total_time += stats.time_ms
total_visited += stats.visited_cells
total_length += stats.path_length
return {
'time_ms': total_time / runs,
'visited_cells': total_visited / runs,
'path_length': total_length / runs
}
def generate_charts(results):
mazes = list(set(r['maze'] for r in results))
alg_names = ['BFS', 'DFS', 'AStar']
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
x = np.arange(len(mazes))
width = 0.25
for i, alg in enumerate(alg_names):
times = []
for m in mazes:
val = next((r['time_ms'] for r in results if r['maze'] == m and r['strategy'] == alg), 0)
times.append(val)
axes[0].bar(x + i * width, times, width, label=alg)
axes[0].set_xlabel('Maze')
axes[0].set_ylabel('Time (ms)')
axes[0].set_title('Execution Time')
axes[0].set_xticks(x + width)
axes[0].set_xticklabels(mazes, rotation=45, ha='right')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
for i, alg in enumerate(alg_names):
visited = []
for m in mazes:
val = next((r['visited_cells'] for r in results if r['maze'] == m and r['strategy'] == alg), 0)
visited.append(val)
axes[1].bar(x + i * width, visited, width, label=alg)
axes[1].set_xlabel('Maze')
axes[1].set_ylabel('Visited Cells')
axes[1].set_title('Visited Nodes')
axes[1].set_xticks(x + width)
axes[1].set_xticklabels(mazes, rotation=45, ha='right')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
for i, alg in enumerate(alg_names):
lengths = []
for m in mazes:
val = next((r['path_length'] for r in results if r['maze'] == m and r['strategy'] == alg), 0)
lengths.append(val)
axes[2].bar(x + i * width, lengths, width, label=alg)
axes[2].set_xlabel('Maze')
axes[2].set_ylabel('Path Length')
axes[2].set_title('Optimality')
axes[2].set_xticks(x + width)
axes[2].set_xticklabels(mazes, rotation=45, ha='right')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('maze_benchmark.png', dpi=150, bbox_inches='tight')
plt.show()
def run_experiments():
test_mazes = [
("maze/level1.txt", "Small 10x6"),
("maze/medium10x10.txt", "Medium 10x10"),
("maze/large20x20.txt", "Large 20x20"),
("maze/empty15x15.txt", "Empty 15x15"),
("maze/no_exit10x10.txt", "No exit 10x10")
]
algorithms = [
("BFS", BFS()),
("DFS", DFS()),
("AStar", AStar())
]
results = []
for filepath, display_name in test_mazes:
print(f"Testing {display_name}...")
for alg_name, alg_obj in algorithms:
try:
stats = run_benchmark(filepath, alg_obj, runs=3)
results.append({
'maze': display_name,
'strategy': alg_name,
'time_ms': stats['time_ms'],
'visited_cells': stats['visited_cells'],
'path_length': stats['path_length']
})
print(f" {alg_name}: time={stats['time_ms']:.3f}ms, visited={stats['visited_cells']:.0f}, length={stats['path_length']:.0f}")
except Exception as e:
print(f" {alg_name}: ERROR - {e}")
results.append({
'maze': display_name,
'strategy': alg_name,
'time_ms': -1,
'visited_cells': -1,
'path_length': -1
})
valid = [r for r in results if r['time_ms'] >= 0]
if not valid:
print("No valid results to save.")
return
with open('maze_experiment.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['maze', 'strategy', 'time_ms', 'visited_cells', 'path_length'])
writer.writeheader()
writer.writerows(valid)
generate_charts(valid)
print("\nResults saved to maze_experiment.csv")
print("Plot saved to maze_benchmark.png")
def play_game():
builder = TxtLabyrinthBuilder()
maze = builder.build_from_file("maze/level1.txt")
walker = Walker(maze.start, maze)
view = ConsoleDisplay(walker)
view._render_maze(maze)
solver = Pathfinder(maze)
solver.attach(view)
print("\n CONTROLS:")
print(" H (left) J (down) K (up) L (right)")
print(" U - undo Q - quit")
print("\n AUTO SEARCH:")
print(" B - BFS D - DFS A - A*")
print("\n" + "=" * 50)
action_stack = []
while True:
cmd = input("\n Command > ").lower()
if cmd == 'q':
print("\n Goodbye!")
break
elif cmd == 'b':
solver.set_algorithm(BFS())
stats = solver.solve()
if stats:
print(f"\n BFS: time={stats.time_ms:.3f}ms, visited={stats.visited_cells}, length={stats.path_length}")
elif cmd == 'd':
solver.set_algorithm(DFS())
stats = solver.solve()
if stats:
print(f"\n DFS: time={stats.time_ms:.3f}ms, visited={stats.visited_cells}, length={stats.path_length}")
elif cmd == 'a':
solver.set_algorithm(AStar())
stats = solver.solve()
if stats:
print(f"\n A*: time={stats.time_ms:.3f}ms, visited={stats.visited_cells}, length={stats.path_length}")
elif cmd in ['h', 'j', 'k', 'l']:
dir_map = {'h': (-1, 0), 'l': (1, 0), 'k': (0, -1), 'j': (0, 1)}
action = MoveAction(walker, dir_map[cmd], maze)
if action.execute():
action_stack.append(action)
view._render_maze_with_player(maze)
if walker.current == maze.exit:
print("\n CONGRATULATIONS! YOU FOUND THE EXIT!")
print(f" Total moves: {len(action_stack)}")
break
else:
print("\n Cannot go there! It's a wall.")
elif cmd == 'u':
if action_stack:
last = action_stack.pop()
last.undo()
view._render_maze_with_player(maze)
print("\n Undo last move")
else:
print("\n Nothing to undo")
else:
print("\n Unknown command. Use h,j,k,l to move, u to undo, q to quit")
print("\n Game over. Thanks for playing!")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] in ('experiment', 'benchmark'):
run_experiments()
else:
play_game()

View File

@ -0,0 +1,15 @@
###############
#S #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# E#
###############

View File

@ -0,0 +1,21 @@
####################
#S #
# ### ##### ##### ##
# # # # # #
### # # ### ### # # #
# # # # # # #
# ### ### # # ### ###
# # # # #
##### ### # ####### #
# # # # #
# ### # ### ### # # #
# # # # # # #
# # ### ### # # ### #
# # # # #
# # ######### # ### #
# # # # # #
# ### # ### # # # # #
# # # # # #
### ### # ### # # ###
# E #
####################

View File

@ -0,0 +1,6 @@
##########
#S #
# ### ####
# # #
# # E#
##########

View File

@ -0,0 +1,10 @@
##########
#S #
# ### ### #
# # # #
### # ### #
# # #
# ### ### #
# # #
# ### ###E#
##########

View File

@ -0,0 +1,11 @@
##########
#S #
# ### ### #
# # # #
### # ### #
# # #
# ### ### #
# # #
# ### ### #
########E #
##########

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@ -0,0 +1,16 @@
maze,strategy,time_ms,visited_cells,path_length
Small 10x6,BFS,0.031851000433865316,24.0,11.0
Small 10x6,DFS,0.01671833342697937,17.0,11.0
Small 10x6,AStar,0.06431333319293724,24.0,11.0
Medium 10x10,BFS,0.04361866679876888,42.0,16.0
Medium 10x10,DFS,0.024233000052239124,26.0,16.0
Medium 10x10,AStar,0.06044533317132542,30.0,16.0
Large 20x20,BFS,0.24542399993758104,211.0,36.0
Large 20x20,DFS,0.2113953335841264,170.0,100.0
Large 20x20,AStar,0.2638656663596824,103.0,36.0
Empty 15x15,BFS,0.19875599991792114,169.0,25.0
Empty 15x15,DFS,0.12158433310105465,169.0,97.0
Empty 15x15,AStar,0.4113716665112103,169.0,25.0
No exit 10x10,BFS,0.0542050001968164,45.0,18.0
No exit 10x10,DFS,0.029572332702324882,28.0,18.0
No exit 10x10,AStar,0.08293900009448407,35.0,18.0
1 maze strategy time_ms visited_cells path_length
2 Small 10x6 BFS 0.031851000433865316 24.0 11.0
3 Small 10x6 DFS 0.01671833342697937 17.0 11.0
4 Small 10x6 AStar 0.06431333319293724 24.0 11.0
5 Medium 10x10 BFS 0.04361866679876888 42.0 16.0
6 Medium 10x10 DFS 0.024233000052239124 26.0 16.0
7 Medium 10x10 AStar 0.06044533317132542 30.0 16.0
8 Large 20x20 BFS 0.24542399993758104 211.0 36.0
9 Large 20x20 DFS 0.2113953335841264 170.0 100.0
10 Large 20x20 AStar 0.2638656663596824 103.0 36.0
11 Empty 15x15 BFS 0.19875599991792114 169.0 25.0
12 Empty 15x15 DFS 0.12158433310105465 169.0 97.0
13 Empty 15x15 AStar 0.4113716665112103 169.0 25.0
14 No exit 10x10 BFS 0.0542050001968164 45.0 18.0
15 No exit 10x10 DFS 0.029572332702324882 28.0 18.0
16 No exit 10x10 AStar 0.08293900009448407 35.0 18.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,66 @@
# Отчёт по лабораторной работе «Структуры данных»
## Цель работы
Реализовать три структуры данных (связный список, хеш-таблицу, двоичное дерево поиска) «с нуля» и экспериментально сравнить их производительность на операциях вставки, поиска и удаления записей телефонного справочника.
## Реализованные структуры
- **Связный список (LinkedList)** элементы хранятся в узлах со ссылкой на следующий.
- **Хеш-таблица (HashTable)** массив корзин фиксированного размера (997), каждая корзина связный список.
- **Двоичное дерево поиска (BST)** узлы содержат ключ (имя) и ссылки на левое/правое поддеревья.
Все операции реализованы вручную без использования классов.
## Методика эксперимента
- **Объём данных**: N = 10000 записей вида `User_XXXXX` → случайный телефон.
- **Режимы ввода**: случайный порядок и отсортированный по имени.
- **Действия**:
1. Вставка всех записей.
2. Поиск 100 существующих + 10 несуществующих имён.
3. Удаление 50 случайных записей.
- **Повторения**: каждый эксперимент выполнен 5 раз, зафиксировано время (`time.perf_counter`).
- **Сбор результатов**: усреднение по 5 повторениям.
## Результаты измерений
### Среднее время операций (секунды)
| Структура | Режим | Вставка | Поиск | Удаление |
|-------------|-------------|----------|----------|----------|
| LinkedList | случайный | 4.0979 | 0.0278 | 0.0134 |
| LinkedList | отсортир. | 3.8044 | 0.0251 | 0.0110 |
| HashTable | случайный | 0.0101 | 0.000080 | 0.000044 |
| HashTable | отсортир. | 0.0098 | 0.000078 | 0.000037 |
| BST | случайный | 0.0229 | 0.000191 | 0.000113 |
| BST | отсортир. | 9.1518 | 0.0824 | 0.0506 |
*Полные замеры всех 5 повторений сохранены в `experiment_results.csv`.*
### График сравнения
![Сравнение производительности](performance_comparison.png)
## Анализ результатов
### Влияние порядка данных на BST
При вставке отсортированных данных BST вырождается в линейный список (высота ≈ N).
Время вставки возрастает с **0.023 с** (случайный) до **9.15 с** (отсортированный) деградация в **~400 раз**.
Поиск и удаление замедляются аналогично.
### Устойчивость хеш-таблицы
Хеш-функция равномерно распределяет ключи независимо от порядка.
Время вставки в случайном (0.0101 с) и отсортированном (0.0098 с) режимах практически одинаково, как и поиск (~0.00008 с).
### Медлительность связного списка
Поиск (O(n)) на 10000 элементов занимает ~0.027 с, что на два порядка медленнее хеш-таблицы.
Вставка в конец также требует прохода по всему списку (~4 с).
### Удаление
Наиболее эффективно в хеш-таблице (≈0.00004 с).
В BST на случайных данных удаление быстрое (0.00011 с), но на отсортированных деградирует до 0.05 с.
В списке удаление (0.013 с) сравнимо с поиском.
## Выводы и рекомендации
1. **Хеш-таблица** оптимальный выбор для задач, где нужен быстрый доступ по ключу, а порядок данных не важен.
2. **Двоичное дерево поиска** подходит, если требуется получать записи в отсортированном порядке **и** данные поступают в случайном порядке. При отсортированных входных данных необходима балансировка (AVL, красно-чёрное дерево).
3. **Связный список** неэффективен для больших объёмов; может применяться только в учебных целях или при очень маленьких коллекциях.
В реальных проектах для справочников и словарей следует выбирать хеш-таблицы или сбалансированные деревья в зависимости от необходимости упорядоченного вывода.

177
semyanovra/docs/report2.md Normal file
View File

@ -0,0 +1,177 @@
# Отчет по лабораторной работе: Поиск выхода из лабиринта
## 1. Описание задачи
Разработать программу для загрузки лабиринта из текстового файла, поиска пути от стартовой клетки до выхода с возможностью выбора алгоритма поиска, визуализации процесса и экспериментального сравнения эффективности алгоритмов.
### Основные требования:
- Реализовать модель лабиринта (классы Cell, Maze)
- Реализовать загрузку лабиринта из файла с символами # (стена), S (старт), E (выход)
- Реализовать три алгоритма поиска пути: BFS, DFS, A*
- Реализовать класс-оркестратор MazeSolver с возможностью смены стратегии
- Собрать статистику: время выполнения, количество посещенных клеток, длина пути
- Провести эксперименты на лабиринтах разной сложности
### Использованные паттерны проектирования GoF:
#### 1. Builder
- **Где используется:** Классы `LabyrinthBuilder` и `TxtLabyrinthBuilder`
- **Почему выбран:** Создание лабиринта из файла включает сложную логику парсинга, валидации и установки старта и выхода. Builder скрывает эти детали от клиента и позволяет легко добавлять новые форматы файлов
- **Преимущества:** При добавлении нового формата достаточно создать новый класс-строитель, не меняя существующие классы Labyrinth и алгоритмы поиска
#### 2. Strategy
- **Где используется:** Классы `SearchAlgorithm`, `BFS`, `DFS`, `AStar`
- **Почему выбран:** Алгоритмы поиска пути взаимозаменяемы и решают одну задачу разными способами. Strategy позволяет динамически менять алгоритм во время выполнения и легко добавлять новые алгоритмы
- **Преимущества:** Класс Pathfinder может использовать любую стратегию через метод set_algorithm. Добавление нового алгоритма требует только создания нового класса
#### 3. Observer
- **Где используется:** Классы `EventListener` и `ConsoleDisplay`
- **Почему выбран:** Приложение должно обновлять консольный интерфейс при различных событиях. Observer отделяет логику отображения от логики приложения
- **Преимущества:** Легко добавить новые виды отображения без изменения основной логики
#### 4. Command
- **Где используется:** Классы `Action` и `MoveAction`
- **Почему выбран:** Для реализации пошагового перемещения игрока с возможностью отмены действий. Command инкапсулирует действие в объект и позволяет реализовать undo и redo
- **Преимущества:** Хранение истории действий и возможность отмены последних ходов без изменения логики класса Walker
## 2. Архитектура приложения
Приложение состоит из следующих основных компонентов:
| Компонент | Назначение |
|-----------|------------|
| `GridCell` | Модель клетки лабиринта (координаты, стена, старт, выход) |
| `Labyrinth` | Модель лабиринта (сетка клеток, методы доступа) |
| `TxtLabyrinthBuilder` | Загрузка лабиринта из текстового файла |
| `BFS`, `DFS`, `AStar` | Алгоритмы поиска пути |
| `Pathfinder` | Оркестратор, управляющий поиском |
| `ConsoleDisplay` | Визуализация лабиринта и игрока |
| `Walker` | Управление позицией игрока |
| `MoveAction` | Команда перемещения с поддержкой Undo |
## 3. Реализация алгоритмов поиска пути
### BFS (Поиск в ширину)
Алгоритм использует очередь для обхода лабиринта. Начинает со стартовой клетки, помещает её в очередь. Затем циклически извлекает клетку из начала очереди, проверяет не является ли она выходом, и добавляет всех непосещенных соседей в конец очереди. **Гарантирует нахождение кратчайшего пути** по количеству шагов.
### DFS (Поиск в глубину)
Алгоритм использует стек для обхода лабиринта. Начинает со стартовой клетки, помещает её в стек. Затем циклически извлекает клетку из конца стека, проверяет не является ли она выходом, и добавляет всех непосещенных соседей в стек. **Не гарантирует нахождение кратчайшего пути**, но обычно быстрее по времени.
### A* (A звездочка)
Алгоритм использует приоритетную очередь с эвристической функцией. Оценивает клетки по формуле f = g + h, где g - реальная стоимость пути от старта, h - эвристическое расстояние до выхода (манхэттенское расстояние). **Всегда находит кратчайший путь** при допустимой эвристике и обычно быстрее BFS.
## 4. Экспериментальная часть
### Тестовые лабиринты
| Имя файла | Размер | Описание |
|-----------|--------|----------|
| level1.txt | 10x6 | Простой лабиринт |
| medium10x10.txt | 10x10 | Лабиринт среднего размера |
| large20x20.txt | 20x20 | Большой запутанный лабиринт |
| empty15x15.txt | 15x15 | Пустой лабиринт без стен |
| no_exit10x10.txt | 10x10 | Лабиринт без достижимого выхода |
### Результаты замеров
Каждый эксперимент проводился 3 раза с усреднением результатов.
| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |
|----------|----------|------------|-----------------|------------|
| Small 10x6 | BFS | 0.032 | 24 | 11 |
| Small 10x6 | DFS | 0.017 | 17 | 11 |
| Small 10x6 | A* | 0.064 | 24 | 11 |
| Medium 10x10 | BFS | 0.044 | 42 | 16 |
| Medium 10x10 | DFS | 0.024 | 26 | 16 |
| Medium 10x10 | A* | 0.060 | 30 | 16 |
| Large 20x20 | BFS | 0.245 | 211 | 36 |
| Large 20x20 | DFS | 0.211 | 170 | 100 |
| Large 20x20 | A* | 0.264 | 103 | 36 |
| Empty 15x15 | BFS | 0.199 | 169 | 25 |
| Empty 15x15 | DFS | 0.122 | 169 | 97 |
| Empty 15x15 | A* | 0.411 | 169 | 25 |
| No exit 10x10 | BFS | 0.054 | 45 | 18 |
| No exit 10x10 | DFS | 0.030 | 28 | 18 |
| No exit 10x10 | A* | 0.083 | 35 | 18 |
### Графики
![Сравнение производительности алгоритмов](maze_benchmark.png)
На графике представлено сравнение трех алгоритмов по трем метрикам: время выполнения (мс), количество посещенных клеток и длина найденного пути.
## 5. Анализ результатов
### Сравнение характеристик алгоритмов
| Характеристика | BFS | DFS | A* |
|----------------|-----|-----|-----|
| Гарантия кратчайшего пути | Да | Нет | Да |
| Скорость на малых лабиринтах | Средняя | Быстрая | Средняя |
| Скорость на больших лабиринтах | Средняя | Быстрая | Средняя |
| Потребление памяти | Высокое | Низкое | Среднее |
| Количество посещенных клеток | Много (211) | Среднее (170) | Мало (103) |
### Детальный анализ по лабиринтам
**Small 10x6:**
- Все алгоритмы нашли оптимальный путь длиной 11 шагов
- DFS оказался самым быстрым (0.017 мс) и посетил меньше всего клеток (17)
- A* посетил больше клеток (24), но нашел оптимальный путь
**Medium 10x10:**
- Оптимальный путь - 16 шагов (BFS и A*)
- DFS нашел путь длиной 16 (в данном случае совпал с оптимальным)
- DFS снова самый быстрый (0.024 мс) и посетил 26 клеток против 42 у BFS
**Large 20x20:**
- BFS и A* нашли оптимальный путь (36 шагов)
- **DFS нашел неоптимальный путь (100 шагов), что на 64 шага длиннее!**
- A* посетил значительно меньше клеток (103 против 211 у BFS)
- Это показывает преимущество эвристики A* на больших лабиринтах
**Empty 15x15:**
- Оптимальный путь - 25 шагов (по прямой)
- DFS нашел путь длиной 97 шагов, что в 3.8 раза длиннее!
- Все алгоритмы посетили одинаковое количество клеток (169) - весь лабиринт
- A* показал самое большое время из-за накладных расходов на эвристику
**No exit 10x10:**
- Все алгоритмы обошли весь достижимый лабиринт
- DFS посетил меньше клеток (28 против 45 у BFS)
- Длина пути 18 показывает, что алгоритмы прошли до тупика
### Ключевые выводы
1. **BFS** - надежный выбор, когда гарантия кратчайшего пути критична. Работает предсказуемо, но на больших лабиринтах посещает много клеток (211 против 103 у A*)
2. **DFS** - самый быстрый алгоритм в большинстве тестов (0.017-0.211 мс), но **ненадежен для поиска оптимального пути**. В большом лабиринте путь оказался на 64% длиннее оптимального!
3. **A*** - лучший баланс. Находит кратчайший путь (как BFS), но посещает на 51% меньше клеток в большом лабиринте. Немного медленнее DFS из-за вычисления эвристики.
### Рекомендации по выбору алгоритма
| Ситуация | Рекомендуемый алгоритм | Обоснование |
|----------|----------------------|-------------|
| Небольшой лабиринт (< 100 клеток) | Любой | Разница в производительности незначительна |
| Большой лабиринт, нужен кратчайший путь | **A*** | Быстрее BFS, посещает меньше клеток |
| Максимальная скорость, путь не важен | **DFS** | Самый быстрый, но может найти длинный путь |
| Лабиринт неизвестной структуры | **A*** | Лучшее соотношение скорость/качество |
## 6. Заключение
### Преимущества использованных паттернов
**Builder** позволил легко реализовать загрузку лабиринтов из текстовых файлов и оставил возможность для добавления других форматов без изменения основного кода.
**Strategy** сделал алгоритмы поиска взаимозаменяемыми. Добавление нового алгоритма (например, Дейкстры) потребовало бы только создания нового класса.
**Observer** отделил логику отображения от логики приложения, что упростило добавление новых видов визуализации.
**Command** позволил реализовать пошаговое управление игроком с возможностью отмены действий без усложнения класса Walker.
### Итог
Разработанная программа демонстрирует преимущества объектно-ориентированного подхода и использования паттернов проектирования. Код является гибким, расширяемым и легко поддерживаемым.
Эксперименты показали, что **A*** является наиболее сбалансированным алгоритмом для поиска пути в лабиринте, обеспечивая оптимальный путь при приемлемой скорости работы и минимальном количестве посещенных клеток. DFS может быть полезен только когда скорость критична, а оптимальность пути не важна. BFS остается надежным выбором для небольших лабиринтов, где простота реализации важнее производительности.

0
shapovalovka/425.txt Normal file
View File

0
shekurovaa/429.md Normal file
View File

0
smirnovad/429.md Normal file
View File

6
sobininaas/429.rtf Normal file
View File

@ -0,0 +1,6 @@
{\rtf1\ansi\ansicpg1251\cocoartf2761
\cocoatextscaling0\cocoaplatform0{\fonttbl}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
}

1
soninrv/428.md Normal file
View File

@ -0,0 +1 @@

0
starikovta/426.md Normal file
View File

1
victorovaas/429 Normal file
View File

@ -0,0 +1 @@
<EFBFBD>¥¦¨¬ ¢כ¢®₪  ×®¬ ­₪ ­  ם×א ­ (ECHO) ¢×«מח¥­.

1
zhigalovrd/425.txt Normal file
View File

@ -0,0 +1 @@
ыфыв