1 (изменено: mafckz, 2016-05-05 13:28:05)

Тема: AHK: Нахождения кратчайшего пути

Нужно написать код для нахождения кратчайшего пути от одной точки до другой (точки могут быть разными).
Вот карта:
http://www.picshare.ru/uploads/160505/ircA2hJ066.jpg
Один переход осуществляется только к ближайшей точки. Необходимо найти путь, где кол-во переходов наименьшее.
Создал массивы: если значение у определенных индексов равно 1 - значит точки по этим индексам соединены.
Не могу сообразить как сделать рекурсию с вычислением.

+ Массивы
Array := Object()
Array.Insert([])
Array[1, 2] := 1
Array[2, 1] := 1
Array[2, 4] := 1
Array[3, 4] := 1
Array[3, 5] := 1
Array[4, 2] := 1
Array[4, 3] := 1
Array[4, 5] := 1
Array[4, 6] := 1
Array[4, 7] := 1
Array[5, 4] := 1
Array[5, 3] := 1
Array[6, 4] := 1
Array[7, 4] := 1

2

Re: AHK: Нахождения кратчайшего пути

А какой смысл в том, что они все одному равны? Как это поможет в вычислении?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3 (изменено: mafckz, 2016-05-05 14:24:57)

Re: AHK: Нахождения кратчайшего пути

Не все. Только те, индексы(=точки) которые соедененны. К примеру, точки 2 и 3 не соединены и элемент Array[2, 3] = 0 (как и Array[3, 2] = 0).
Как поможет? Не знаю, сам думаю.

4

Re: AHK: Нахождения кратчайшего пути

То-есть, у вас предполагается создать и другие массивы, которые не будут равны 1?
Это откуда задача? Задали где-то, или для развлечениия?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5 (изменено: mafckz, 2016-05-05 14:37:24)

Re: AHK: Нахождения кратчайшего пути

Других массивов не предполагается, остальные элементы (не приравненные к 1) по дефолту равны "" (ничему). Это я для знакомого пишу бота к браузерной игре. А суть кода, найти путь для перемещения по локациям. Там локаций штук 20, все варианты нереально прописать (как для примера на схеме).

6

Re: AHK: Нахождения кратчайшего пути

mafckz пишет:

Других массивов не предполагается, остальные элементы (не приравненные к 1) по дефолту равны "" (ничему)

Здесь противоречие. Так предполагается, или нет? Если будут другие элементы, тогда есть смысл, если нет — никакого. Мне представляется, вам нужно что-то такое:

Points := { 1: [2], 2: [1,4], 3:[4,5], 4: [2,3,5], 5: [3,4], 6: [4], 7: [4] }
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

7

Re: AHK: Нахождения кратчайшего пути

Во-первых, "кратчайших" путей может быть несколько.
Во-вторых, странный массив. Проще было бы заполнять такой:

Point1 := [2]
Point2 := [1, 4]
Point3 := [4, 5]
Point4 := [2, 3, 5, 6, 7]
Point5 := [3, 4]
Point6 := [4]
Point7 := [4]

Или как у teadrinker'а.
В-третьих, переношу тему в соответствующий раздел.

8

Re: AHK: Нахождения кратчайшего пути

Да, ошибся немного:

Points := { 1: [2], 2: [1,4], 3: [4,5], 4: [2,3,5,6,7], 5: [3,4], 6: [4], 7: [4] }
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

9 (изменено: teadrinker, 2016-05-07 21:35:36)

Re: AHK: Нахождения кратчайшего пути

Как-то так:

    http://i.imgur.com/9weEE9S.jpg

Points := { 1: [2,4], 2: [1,3], 3: [2,4,7,8], 4: [1,3,5,6], 5: [4,6], 6: [4,5], 7: [3,8,10], 8: [3,7,9], 9: [8], 10: [7] }

PointsObj := new ShortWay(Points)
ways := PointsObj.GetShortWays(10, 1)

for k, v in ways  {
	text .= A_Index = 1 ? "" : "`n"
	for k, v in v
		text .= (A_Index = 1 ? "" : " —> ") . v
}
MsgBox, % text

class ShortWay
{
	__New(points)  {
		this.points := points
		this.ways := []
	}
	GetShortWays(start, end)  {
		this.SearchWays(start, end, [start])
		Return this.ways
	}
	SearchWays(p1, p2, way = "", prev = "")  {
		m := this.ways.1.MaxIndex()
		if ( (m && (way.MaxIndex() > m - 1)) || !(jumps := this.GetJumps(p1, prev, p2, way)) )
			Return
		
		if !IsObject(jumps)  {
			way.Push(jumps)
			( m && way.MaxIndex() < m && this.ways := [] )
			this.ways.Push(way)
			Return
		}
		for k, v in jumps
			node := way.Clone(), node.Push(v), this.SearchWays(v, p2, node, p1)
	}
	GetJumps(p, prev, end, way)  {
		jumps := []
		for k, v in this.points[p]  {
			if (v = end)
				Return v
			
			for i, c in way
				if (v = c)
					continue 2
			
			( this.points[v].MaxIndex() > 1 && v != prev && jumps.Push(v) )
		}
		Return jumps
	}
}

Скорее всего, перемудрил немного, наверное можно подсократить, но лень думать.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10 (изменено: serzh82saratov, 2016-05-06 01:58:41)

Re: AHK: Нахождения кратчайшего пути

У меня такое получилось. Есть отличие, если задать From и To одинаковыми, и эта точка не в тупике, то будут показаны кольцевые пути до неё, хотя не знаю зачем бы оно могло пригодится.


Map := { 1: [2,4], 2: [1,3], 3: [2,4,7,8], 4: [1,3,5,6], 5: [4,6], 6: [4,5], 7: [3,8,10], 8: [3,7,9], 9: [8], 10: [7] }

From := 9
To := 1

paths := Path(From, To, Map)

for k, v in paths  {
	text .= A_Index = 1 ? "" : "`n"
	for k, v in v
		text .= (A_Index = 1 ? "" : ", ") . v
}
MsgBox, % text
Return


Path(From, To, Map) {
	For k, v in Map[From]
		If (v = To)
			Return [[From, To]]
	Finds := ParseMap(Map, {To:To,Path:[From],pI:[1],Find:[],Finds:0,Exist:{}})
	For k, v in Finds
		Min := !Min || Min > v.MaxIndex() ? v.MaxIndex() : Min
	For k, v in Finds, Ret := []
		v.MaxIndex() = Min ? Ret.Push(v) : 0
	Return Ret
}

ParseMap(Map, O) {
	pM := O.Path.MaxIndex(), pI := O.pI[pM]
	P := O.Path[pM], N := Map[P][pI]
	If (P = O.Path[1] && pI > Map[P].MaxIndex())
		Return O.Find
	If (N = O.To && (O.To = O.Path[1] ? O.Path.MaxIndex() > 2 : 1))
		O.Find[++O.Finds] := [], O.Find[O.Finds].Push(O.Path*), O.Find[O.Finds].Push(N), ++O.pI[pM]
	Else If (O.Exist[N] || N = O.Path[1])
		++O.pI[pM]
	Else If (N = "")
		O.Path.Pop(), O.pI.Pop(), ++O.pI[O.Path.MaxIndex()], O.Exist[P] := 0
	Else
		O.Path.Push(N), O.pI.Push(1), O.Exist[N] := 1
	Return ParseMap(Map, O)
}
По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

11

Re: AHK: Нахождения кратчайшего пути

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

12

Re: AHK: Нахождения кратчайшего пути

Но если задать:

PointsObj.GetShortPaths(1, 1)
1, 2, 1
1, 4, 1

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

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

13

Re: AHK: Нахождения кратчайшего пути

Да, странно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

14 (изменено: mafckz, 2016-05-06 09:25:29)

Re: AHK: Нахождения кратчайшего пути

teadrinker, serzh82saratov, большое спасибо за помощь и потраченное время. Вы такие задачи сразу в редакторе и в уме решаете, или предварительно строите алгоритм?

Скрипт полностью выполняет поставленную цель. (Если, кто-нибудь еще будет пробовать запускать скрипт, то нужно включать #NoEnv - иначе не работает (в коде teadrinker'a)).

15

Re: AHK: Нахождения кратчайшего пути

mafckz пишет:

(Если, кто-нибудь еще будет пробовать запускать скрипт, то нужно включать #NoEnv - иначе не работает (в коде teadrinker'a)).

А у меня почему-то #NoEnv по умолчанию уже включено. А так да, там я по невнимательности системную переменную path использую, потом подправлю.
Алгоритм заранее не продумывал, думал в том же порядке, в котором писал.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

16

Re: AHK: Нахождения кратчайшего пути

Подправил.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

17

Re: AHK: Нахождения кратчайшего пути

serzh82saratov, не, твой не фурычет. Попробуй:
http://i.imgur.com/vuR3n5B.jpg

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

18

Re: AHK: Нахождения кратчайшего пути

А массив и пример точек можешь выложить,  я вечерком гляну.

Я же говорил задача занимательная.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

19

Re: AHK: Нахождения кратчайшего пути

А, забыл:

Points := { 1: [9,10], 2: [3,9], 3: [2,8], 4: [5,8,16], 5: [4,7,12], 6: [7,20], 7: [5,6], 8: [3,4,14,20], 9: [1,2,10,12,14], 10: [1,9,11], 11: [10,12]
			, 12: [5,9,11,13], 13: [12,14,15], 14: [8,9,13,15], 15: [13,14,16], 16: [4,15,17,20], 17: [16,18], 18: [17,19], 19: [18,20], 20: [6,8,16,19] }
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

20

Re: AHK: Нахождения кратчайшего пути

С любыми точками не работает?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

21

Re: AHK: Нахождения кратчайшего пути

С 1,9 — работает, с 1, 2 — нет, зацикливается.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

22

Re: AHK: Нахождения кратчайшего пути

Вот, собственно, виртуальная карта точек длиной length и количеством связей между точками от 1 до MaxLinks:

Map := CreateMap(length, MaxLinks)

CreateMap(len, max)
{
	arr := []
	Loop % len
		arr[A_Index] := []
	IndexArr := []
	Loop % len
		IndexArr[A_Index] := A_Index
	
	Loop % len  {
		i := A_Index
		m := arr[i].MaxIndex(), (!m && m := 0)
		rest := max - m
		if (rest = 0)
			continue
		
		WithoutCurrent := IndexArr.Clone()
		RemoveFromIdx(i, WithoutCurrent)
		
		Random, rand, 1, rest
		for k, v in keys := GetRandomNumbers(WithoutCurrent.MaxIndex(), rand)  {
			idx := WithoutCurrent[v]
			arr[idx].Push(i)
			( arr[idx].MaxIndex() >= max && RemoveFromIdx(idx, IndexArr) )
			arr[i].Push(idx)
		}
		( arr[i].MaxIndex() >= max && RemoveFromIdx(i, IndexArr) )
	}
	Return arr
}

RemoveFromIdx(n, arr)
{
	for k, v in arr
		if (v = n)
			break
	arr.RemoveAt(k)
}

GetRandomNumbers(len, num)
{
	arr := []
	Loop % len
		arr[A_Index] := A_Index
	RetArr := []
	Loop % num  {
		Random, rand, 1, arr.MaxIndex()
		RetArr.Push(arr.RemoveAt(rand))
	}
	Return RetArr
}

Я слегка оптимизировал свой вариант, он легко справляется с массивом в 400 точек с количеством связей от 1 до 5:

SetBatchLines, -1
length := 400
MaxLinks := 5

Map := CreateMap(length, MaxLinks)
p := GetRandomNumbers(length, 2)

PointsObj := new ShortWay(Map)
ways := PointsObj.GetShortWays(p[1], p[2])

for k, v in ways  {
	text .= A_Index = 1 ? "" : "`n"
	for k, v in v
		text .= (A_Index = 1 ? "" : " —> ") . v
}
MsgBox, % text

CreateMap(len, max)
{
	arr := []
	Loop % len
		arr[A_Index] := []
	IndexArr := []
	Loop % len
		IndexArr[A_Index] := A_Index
	
	Loop % len  {
		i := A_Index
		m := arr[i].MaxIndex(), (!m && m := 0)
		rest := max - m
		if (rest = 0)
			continue
		
		WithoutCurrent := IndexArr.Clone()
		RemoveFromIdx(i, WithoutCurrent)
		
		Random, rand, 1, rest
		for k, v in keys := GetRandomNumbers(WithoutCurrent.MaxIndex(), rand)  {
			idx := WithoutCurrent[v]
			arr[idx].Push(i)
			( arr[idx].MaxIndex() >= max && RemoveFromIdx(idx, IndexArr) )
			arr[i].Push(idx)
		}
		( arr[i].MaxIndex() >= max && RemoveFromIdx(i, IndexArr) )
	}
	Return arr
}

RemoveFromIdx(n, arr)
{
	for k, v in arr
		if (v = n)
			break
	arr.RemoveAt(k)
}

GetRandomNumbers(len, num)
{
	arr := []
	Loop % len
		arr[A_Index] := A_Index
	RetArr := []
	Loop % num  {
		Random, rand, 1, arr.MaxIndex()
		RetArr.Push(arr.RemoveAt(rand))
	}
	Return RetArr
}

class ShortWay
{
	__New(points)  {
		this.points := points
		this.ways := []
	}
	GetShortWays(start, end)  {
		this.SearchWays(start, end, [start])
		Return this.ways
	}
	SearchWays(p1, p2, way = "", prev = "")  {
		m := this.ways.1.MaxIndex()
		if ( (m && (way.MaxIndex() > m - 1)) || !(jumps := this.GetJumps(p1, prev, p2, way)) )
			Return
		
		if !IsObject(jumps)  {
			way.Push(jumps)
			( m && way.MaxIndex() < m && this.ways := [] )
			this.ways.Push(way)
			Return
		}
		for k, v in jumps
			node := way.Clone(), node.Push(v), this.SearchWays(v, p2, node, p1)
	}
	GetJumps(p, prev, end, way)  {
		jumps := []
		for k, v in this.points[p]  {
			if (v = end)
				Return v
			
			for i, c in way
				if (v = c)
					continue 2
			
			( this.points[v].MaxIndex() > 1 && v != prev && jumps.Push(v) )
		}
		Return jumps
	}
}

Но если точек больше, иногда крашится. Похоже, по-хорошему надо заменять рекурсию на цикл.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

23 (изменено: serzh82saratov, 2016-05-08 02:17:08)

Re: AHK: Нахождения кратчайшего пути

teadrinker пишет:

Похоже, по-хорошему надо заменять рекурсию на цикл.

Так и знал что теперь спрошу, а в чём отличие по твоему?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

24

Re: AHK: Нахождения кратчайшего пути

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

25 (изменено: serzh82saratov, 2016-05-08 02:28:23)

Re: AHK: Нахождения кратчайшего пути

wikipedia пишет:

Эта ошибка случается по трём причинам.

Тут не осилил, может #MaxMem?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

26

Re: AHK: Нахождения кратчайшего пути

Нет, не должно помочь, это для переменных, но не для стека.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

27

Re: AHK: Нахождения кратчайшего пути

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

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

28 (изменено: serzh82saratov, 2016-05-10 04:48:23)

Re: AHK: Нахождения кратчайшего пути

teadrinker пишет:

Но если точек больше, иногда крашится. Похоже, по-хорошему надо заменять рекурсию на цикл.

Вроде так, проверил на больших значениях. Постарался максимально оптимизировать, перебора всех возможных путей нет, как в прошлом варианте. Возвращает один путь (первый найденный с минимальными номерами точек), это заметно ускоряет вычисления.
Результат можно вернуть в виде текста а не массива, указав 4 параметр в true.


Map := { 1: [9,10], 2: [3,9], 3: [2,8], 4: [5,8,16], 5: [4,7,12], 6: [7,20], 7: [5,6], 8: [3,4,14,20], 9: [1,2,10,12,14], 10: [1,9,11], 11: [10,12]
		, 12: [5,9,11,13], 13: [12,14,15], 14: [8,9,13,15], 15: [13,14,16], 16: [4,15,17,20], 17: [16,18], 18: [17,19], 19: [18,20], 20: [6,8,16,19] }

From := 1
To := 19
MsgBox % ShortPath(From, To, Map, 1) 
Return

ShortPath(From, To, Map, RetText = 0) {
	Bridge := [From], Index := [1], Exist := []
	Loop
	{
		M := Bridge.MaxIndex(), B := Bridge[M], I := Index[M], N := Map[b][i], Exist[b] := 1
		If (M = 1 && !N)
			Break
		If (!N || Min && M >= Min)
		{
			Exist[Bridge.Pop()] := 0, Index.Pop(), ++Index[Bridge.MaxIndex()]
			Continue
		}
 		; If (I = 1 && (From = To ? M > 2 : 1))  ; для кольцевых маршрутов
		If (I = 1) 
			For k, v in Map[b]
				If (v = To)
				{ 
					Finds := Bridge.Clone(), Finds.Push(To), Min := M
					If (M < 3)
						Break 2
					Exist[Bridge.Pop()] := 0, Exist[Bridge.Pop()] := 0
					Index.Pop(), Index.Pop(), ++Index[Bridge.MaxIndex()]
					Continue 2
				}
		If Exist[N]
			++Index[M]
		Else
			Bridge.Push(N), Index.Push(1)
	}
	If RetText
		For k, v in Finds
			Finds .= (A_Index = 1 ? "" : ", ") . v
	Return Finds 
}

Вариант под спойлером возвращает все короткие пути, но само собой на больших картах будет искать заметно дольше.

+ открыть спойлер

Map := { 1: [9,10], 2: [3,9], 3: [2,8], 4: [5,8,16], 5: [4,7,12], 6: [7,20], 7: [5,6], 8: [3,4,14,20], 9: [1,2,10,12,14], 10: [1,9,11], 11: [10,12]
		, 12: [5,9,11,13], 13: [12,14,15], 14: [8,9,13,15], 15: [13,14,16], 16: [4,15,17,20], 17: [16,18], 18: [17,19], 19: [18,20], 20: [6,8,16,19] }

From := 2
To := 13 

MsgBox % ShortPath(From, To, Map, 1) 
Return


ShortPath(From, To, Map, RetText = 0) {
	Bridge := [From], Index := [1], Exist := [], Finds := []
	Loop
	{
		M := Bridge.MaxIndex(), B := Bridge[M], I := Index[M], N := Map[b][i], Exist[b] := 1
		If (M = 1 && !N)
			Break
		If (!N || Min && M > Min)
		{
			Exist[Bridge.Pop()] := 0, Index.Pop(), ++Index[Bridge.MaxIndex()]
			Continue
		}
		 ; If (I = 1 && (From = To ? M > 2 : 1))  ; для кольцевых маршрутов
		If (I = 1) 
			For k, v in Map[b]
				If (v = To)
				{  
					Find := Bridge.Clone(), Find.Push(To), Finds.Push(Find), Min := M 
					If (M = 1)
						Break 2
					Exist[Bridge.Pop()] := 0, Index.Pop(), ++Index[Bridge.MaxIndex()]
					Continue 2
				}
		If Exist[N]
			++Index[M]
		Else
			Bridge.Push(N), Index.Push(1)
	}
	For k, v in Finds, Ret := []
		v.MaxIndex() = Min + 1 ? Ret.Push(v) : 0
	If RetText
		For k, v in Ret 
			For k, v in v, Ret .= A_Index = 1 ? "" : "`n"
				Ret .= (A_Index = 1 ? "" : ", ") . v
	Return Ret 
}
По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

29

Re: AHK: Нахождения кратчайшего пути

А у меня пока не было времени, завтра тоже попробую.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

30

Re: AHK: Нахождения кратчайшего пути

У меня пока так вышло:

Points := { 1: [9,10], 2: [3,9], 3: [2,8], 4: [5,8,16], 5: [4,7,12], 6: [7,20], 7: [5,6], 8: [3,4,14,20], 9: [1,2,10,12,14], 10: [1,9,11], 11: [10,12]
			, 12: [5,9,11,13], 13: [12,14,15], 14: [8,9,13,15], 15: [13,14,16], 16: [4,15,17,20], 17: [16,18], 18: [17,19], 19: [18,20], 20: [6,8,16,19] }
			
ways := GetShortWays(Points, 11, 20)

for k, v in ways  {
	text .= A_Index = 1 ? "" : "`n"
	for k, v in v
		text .= (A_Index = 1 ? "" : " —> ") . v
}
MsgBox, % text

GetShortWays(points, start, end)
{
	max := 4
	Loop  {
		if !IsObject(jumps := GetJumps(points, start, end, [start]))
			Return [[start, end]]
		ways := [], CurrentNode := {way: [start], jumps: jumps, PrevNode: ""}, max *= 2
		while CurrentNode  {
			m := ways.1.MaxIndex(), c := CurrentNode.way.MaxIndex()
			if ( c + 1 = m || c > max || !(NextPoint := CurrentNode.jumps.Pop()) )  {
				CurrentNode := CurrentNode.PrevNode
				continue
			}
			way := CurrentNode.way.Clone()
			if jumps := GetJumps(points, NextPoint, end, way)  {
				if IsObject(jumps) && way.Push(NextPoint)
					CurrentNode := {way: way, jumps: jumps, PrevNode: CurrentNode}
				else
					way.Push(NextPoint, jumps), (way.MaxIndex() < m && ways := []), ways.Push(way)
			}
		}
	} until ways.1.1 || max > points.MaxIndex()
	Return ways
}
	
GetJumps(points, p, end, way)
{
	jumps := []
	for k, v in points[p]  {
		if (v = end)
			Return v
		
		for i, c in way
			if (v = c)
				continue 2
		
		jumps.Push(v)
	}
	Return jumps
}

Можно посоревноваться:

SetBatchLines, -1
length := 1000
MaxLinks := 5

Map := CreateMap(length, MaxLinks)
p := GetRandomNumbers(length, 2)

start := A_TickCount
text1 := GetWaysText(GetShortWays(Map, p[1], p[2]))
time1 := A_TickCount - start
SoundBeep
start := A_TickCount
text2 := ShortPath(p[1], p[2], Map, 1)
time2 := A_TickCount - start

MsgBox, % "teadrinker:`n`nTime = " time1 "`n" text1 "`n`nserzh82saratov:`n`nTime = " time2 "`n" text2

GetShortWays(points, start, end)
{
	max := 4
	Loop  {
		if !IsObject(jumps := GetJumps(points, start, end, [start]))
			Return [[start, end]]
		ways := [], CurrentNode := {way: [start], jumps: jumps, PrevNode: ""}, max *= 2
		while CurrentNode  {
			m := ways.1.MaxIndex(), c := CurrentNode.way.MaxIndex()
			if ( c + 1 = m || c > max || !(NextPoint := CurrentNode.jumps.Pop()) )  {
				CurrentNode := CurrentNode.PrevNode
				continue
			}
			way := CurrentNode.way.Clone()
			if jumps := GetJumps(points, NextPoint, end, way)  {
				if IsObject(jumps) && way.Push(NextPoint)
					CurrentNode := {way: way, jumps: jumps, PrevNode: CurrentNode}
				else
					way.Push(NextPoint, jumps), (way.MaxIndex() < m && ways := []), ways.Push(way)
			}
		}
	} until ways.1.1 || max > points.MaxIndex()
	Return ways
}
	
GetJumps(points, p, end, way)
{
	jumps := []
	for k, v in points[p]  {
		if (v = end)
			Return v
		
		for i, c in way
			if (v = c)
				continue 2
		
		jumps.Push(v)
	}
	Return jumps
}

GetWaysText(ways)
{
	for k, v in ways  {
		text .= A_Index = 1 ? "" : "`n"
		for k, v in v
			text .= (A_Index = 1 ? "" : ", ") . v
	}
	Return text
}

;***********************************************************************************************************

ShortPath(From, To, Map, RetText = 0) {
	Bridge := [From], Index := [1], Exist := [], Finds := []
	Loop
	{
		M := Bridge.MaxIndex(), B := Bridge[M], I := Index[M], N := Map[b][i], Exist[b] := 1
		If (M = 1 && !N)
			Break
		If (!N || Min && M > Min)
		{
			Exist[Bridge.Pop()] := 0, Index.Pop(), ++Index[Bridge.MaxIndex()]
			Continue
		}
		 ; If (I = 1 && (From = To ? M > 2 : 1))  ; для кольцевых маршрутов
		If (I = 1) 
			For k, v in Map[b]
				If (v = To)
				{  
					Find := Bridge.Clone(), Find.Push(To), Finds.Push(Find), Min := M 
					If (M = 1)
						Break 2
					Exist[Bridge.Pop()] := 0, Index.Pop(), ++Index[Bridge.MaxIndex()]
					Continue 2
				}
		If Exist[N]
			++Index[M]
		Else
			Bridge.Push(N), Index.Push(1)
	}
	For k, v in Finds, Ret := []
		v.MaxIndex() = Min + 1 ? Ret.Push(v) : 0
	If RetText
		For k, v in Ret 
			For k, v in v, Ret .= A_Index = 1 ? "" : "`n"
				Ret .= (A_Index = 1 ? "" : ", ") . v
	Return Ret 
}

;********************************************************************************************************************

CreateMap(len, max)
{
	arr := []
	Loop % len
		arr[A_Index] := []
	IndexArr := []
	Loop % len
		IndexArr[A_Index] := A_Index
	
	Loop % len  {
		i := A_Index
		m := arr[i].MaxIndex(), (!m && m := 0)
		rest := max - m
		if (rest = 0)
			continue
		
		WithoutCurrent := IndexArr.Clone()
		RemoveFromIdx(i, WithoutCurrent)
		
		Random, rand, 1, rest
		for k, v in keys := GetRandomNumbers(WithoutCurrent.MaxIndex(), rand)  {
			idx := WithoutCurrent[v]
			arr[idx].Push(i)
			( arr[idx].MaxIndex() >= max && RemoveFromIdx(idx, IndexArr) )
			arr[i].Push(idx)
		}
		( arr[i].MaxIndex() >= max && RemoveFromIdx(i, IndexArr) )
	}
	Return arr
}

RemoveFromIdx(n, arr)
{
	for k, v in arr
		if (v = n)
			break
	arr.RemoveAt(k)
}

GetRandomNumbers(len, num)
{
	arr := []
	Loop % len
		arr[A_Index] := A_Index
	RetArr := []
	Loop % num  {
		Random, rand, 1, arr.MaxIndex()
		RetArr.Push(arr.RemoveAt(rand))
	}
	Return RetArr
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

31

Re: AHK: Нахождения кратчайшего пути

teadrinker пишет:

Можно посоревноваться:

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

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

32

Re: AHK: Нахождения кратчайшего пути

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

33

Re: AHK: Нахождения кратчайшего пути

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

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

34 (изменено: serzh82saratov, 2016-05-20 03:33:54)

Re: AHK: Нахождения кратчайшего пути

Вот ещё быстрее и проще.

Сравнение на вычислениях более 2 секунд.


#SingleInstance Force
#NoEnv

 
SetBatchLines, -1
length := 2000
MaxLinks := 111

start:
Map := CreateMap(length, MaxLinks)
p := GetRandomNumbers(length, 2)

start := A_TickCount
text1 := GetWaysText(GetShortWays(Map, p[1], p[2]))
time1 := A_TickCount - start
RegExReplace(text1, "m`a)$", "", Count1)
SoundBeep
if !(time1 > 2000)
	goto start
SoundBeep
start := A_TickCount
text2 := AllShortPath(p[1], p[2], Map, 1)
time2 := A_TickCount - start
RegExReplace(text2, "m`a)$", "", Count2)

MsgBox, % "teadrinker:`nTime = " time1 "`nCount = " Count1 "`n`n" text1 "`n`nserzh82saratov:`nTime = " time2 "`nCount = " Count2 "`n`n" text2
Return

GetShortWays(points, start, end)
{
	max := 4
	Loop  {
		if !IsObject(jumps := GetJumps(points, start, end, [start]))
			Return [[start, end]]
		ways := [], CurrentNode := {way: [start], jumps: jumps, PrevNode: ""}, max *= 2
		while CurrentNode  {
			m := ways.1.MaxIndex(), c := CurrentNode.way.MaxIndex()
			if ( c + 1 = m || c > max || !(NextPoint := CurrentNode.jumps.Pop()) )  {
				CurrentNode := CurrentNode.PrevNode
				continue
			}
			way := CurrentNode.way.Clone()
			if jumps := GetJumps(points, NextPoint, end, way)  {
				if IsObject(jumps) && way.Push(NextPoint)
					CurrentNode := {way: way, jumps: jumps, PrevNode: CurrentNode}
				else
					way.Push(NextPoint, jumps), (way.MaxIndex() < m && ways := []), ways.Push(way)
			}
		}
	} until ways.1.1 || max > points.MaxIndex()
	Return ways
}
	
GetJumps(points, p, end, way)
{
	jumps := []
	for k, v in points[p]  {
		if (v = end)
			Return v
		
		for i, c in way
			if (v = c)
				continue 2
		
		jumps.Push(v)
	}
	Return jumps
}

GetWaysText(ways)
{
	for k, v in ways  {
		text .= A_Index = 1 ? "" : "`n"
		for k, v in v
			text .= (A_Index = 1 ? "" : ", ") . v
	}
	Return text
}

;***********************************************************************************************************

AllShortPath(From, To, Map, RetText = 0) {
	Bridge := [[From]], Find := [], Exist := []
	While !Find.MaxIndex() && Bridge.MaxIndex() {
		For k, v in Bridge
			Exist[v[v.MaxIndex()]] := 1
		For k, b in Bridge.Clone(), Bridge := []
			For k, p in Map[b[b.MaxIndex()]]
				If !Exist[p] && (a := b.Clone(), a.Push(p))
					If (p = To && Find.Push(a)) || !Bridge.Push(a)
						Break
	} If RetText
		For k, v in Find
			For k, v in v, Find .= A_Index = 1 ? "" : "`n"
				Find .= (A_Index = 1 ? "" : ", ") . v
	Return Find
}

;********************************************************************************************************************

CreateMap(len, max)
{
	arr := []
	Loop % len
		arr[A_Index] := []
	IndexArr := []
	Loop % len
		IndexArr[A_Index] := A_Index
	
	Loop % len  {
		i := A_Index
		m := arr[i].MaxIndex(), (!m && m := 0)
		rest := max - m
		if (rest = 0)
			continue
		
		WithoutCurrent := IndexArr.Clone()
		RemoveFromIdx(i, WithoutCurrent)
		
		Random, rand, 1, rest
		for k, v in keys := GetRandomNumbers(WithoutCurrent.MaxIndex(), rand)  {
			idx := WithoutCurrent[v]
			arr[idx].Push(i)
			( arr[idx].MaxIndex() >= max && RemoveFromIdx(idx, IndexArr) )
			arr[i].Push(idx)
		}
		( arr[i].MaxIndex() >= max && RemoveFromIdx(i, IndexArr) )
	}
	Return arr
}

RemoveFromIdx(n, arr)
{
	for k, v in arr
		if (v = n)
			break
	arr.RemoveAt(k)
}

GetRandomNumbers(len, num)
{
	arr := []
	Loop % len
		arr[A_Index] := A_Index
	RetArr := []
	Loop % num  {
		Random, rand, 1, arr.MaxIndex()
		RetArr.Push(arr.RemoveAt(rand))
	}
	Return RetArr
} 

Функция.

AllShortPath(From, To, Map, RetText = 0) {
	Bridge := [[From]], Find := [], Exist := []
	While !Find.MaxIndex() && Bridge.MaxIndex() {
		For k, v in Bridge
			Exist[v[v.MaxIndex()]] := 1
		For k, b in Bridge.Clone(), Bridge := []
			For k, p in Map[b[b.MaxIndex()]]
				If !Exist[p] && (a := b.Clone(), a.Push(p))
					If (p = To && Find.Push(a)) || !Bridge.Push(a)
						Break
	} If RetText
		For k, v in Find
			For k, v in v, Find .= A_Index = 1 ? "" : "`n"
				Find .= (A_Index = 1 ? "" : ", ") . v
	Return Find
}

Или вариант пошустрее, если нужен только один путь.

ShortPath(From, To, Map, RetText = 0) {
	Bridge := [[From]], Exist := [], Exist[From] := 1
	While Bridge.MaxIndex()
		For k, b in Bridge.Clone(), Bridge := [] 
			For k, p in Map[b[b.MaxIndex()]]
				If !Exist[p] && (a := b.Clone(), a.Push(p))
					If (p = To && Find := a) || !(Bridge.Push(a) && Exist[p] := 1)
						Break 3
	If RetText
		For k, v in Find
			Find .= (k = 1 ? "" : ", ") . v
	Return Find
}
По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui