Memory 旅行推销员在F#
我的问题不在于算法本身,而在于F#达到极限时的性能。在这个例子中是一个程序,我试图用蛮力解决25个城市的问题(动态规划) (对于一个小例子来说,该算法似乎工作得很好,我相信它能完成任务) 我正在控制台输出窗口中打印一个count变量以监视进度。 正如预期的那样,在第一次迭代到Memory 旅行推销员在F#,memory,f#,mono,garbage-collection,xamarin-studio,Memory,F#,Mono,Garbage Collection,Xamarin Studio,我的问题不在于算法本身,而在于F#达到极限时的性能。在这个例子中是一个程序,我试图用蛮力解决25个城市的问题(动态规划) (对于一个小例子来说,该算法似乎工作得很好,我相信它能完成任务) 我正在控制台输出窗口中打印一个count变量以监视进度。 正如预期的那样,在第一次迭代到m=12时,运行时间呈指数增长 m=2 1887ms; m=3 1870ms; m=4 1902ms; m=5 2074ms; m=6 2954ms; m=7 6261ms; m=8 16746ms; m=9 38442ms
m=12
时,运行时间呈指数增长
m=2
1887ms;
m=3
1870ms;
m=4
1902ms;
m=5
2074ms;
m=6
2954ms;
m=7
6261ms;
m=8
16746ms;
m=9
38442ms;
m=10
80396ms;
m=11
140985ms;
m=12
207950ms;
因此,尽管迭代13之前的性能并不出色,但至少它看起来是“可管理的”。事实上,如果我的理解是正确的,迭代12和13是CPU最重的。尽管如此,我还是希望从执行模式来看,该迭代的执行时间大约为300000毫秒,但事实并非如此
在MacOS X 10.11.3 El Capitan上运行的我的(新)iMAC视网膜的控制监视器上缩放,配备3.3Ghz i7四核、16 GB RAM和在Xamarin.Studio 6.0内运行的SSD。我看到程序的内存使用量是一个很大的3 GB。它根本没有经过优化,但它应该在机器的能力范围内,不应该成为负担
m=13
进展非常非常缓慢,以这样的速度计算似乎需要几个小时。在这个阶段,在监视器的CPU端,它表示进程使用了105%的CPU(左边第一列)。1小时后(完成迭代的2/3),它崩溃了,并显示以下消息:
错误:垃圾收集器无法为分配16384字节的内存
主堆部分
我有点惊讶我需要做垃圾收集,因为内存看起来不是主要问题
我正在定义一个巨大的Array2D
,其中有2^24个条目和24列=17M*24个条目(float32*sbyte),每个条目使用32+8=40位=5字节,因此为2GB
也许这就是问题所在,我在\u size\u m循环的子集\u中为S做了一个
这必须在迭代13时完成2700000次(在1860000时停止),在这个循环中,我对列表进行了一些赋值。也许那里需要垃圾收集
我以前从未在F#中这样做过(仅在R中,它足以不时编写命令GC()
或remove(object)
。在控制监视器中进行监视,与可用RAM相比,内存似乎从来都不是问题?发生了什么
我知道我也许应该找到一个更好的算法,它的内存占用更少,但无论如何,这是了解这个问题的好机会,与其他解决问题的人相比,如果一切按计划进行,执行时间对于第一次(残酷的)尝试来说并不是完全荒谬的
这是源代码
//////// Travelling Salesman problem ////////
open System
open System.Collections
open System.Collections.Generic
open System.IO
open System.Windows
open FSharp.Charting
exception InnerError of string
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
///////////////// preparing the data /////////////////
// format of the files
//[number_of_cities]
//[x_1] [y_1] // coordinate
let x = File.ReadAllLines "/Users/francois-guillaume.rideau/Documents/MOOC/TSP.txt"
let split (text:string)=
text.Split [|'\t';' '|]
let splitInto2Values (A: string []) =
(float A.[0],float A.[1])
let parseLine (line:string) =
line
|> split
|> splitInto2Values
let num_cities = int x.[0] // 25
let cities = x |> Array.tail |> Array.map parseLine // [x_1][y_1]
let dist_matrix = Array2D.create num_cities num_cities 0.0f
for i in 0..(num_cities-1)do
for j in 0..(num_cities-1) do
dist_matrix.[i,j]<- float32 (sqrt ( (fst cities.[i] - fst cities.[j])*(fst cities.[i] - fst cities.[j]) + (snd cities.[i] - snd cities.[j])*(snd cities.[i] - snd cities.[j]) ))
let arrayOfArrays = [| [| 0.0f; 2.0f;1.0f;4.0f |]; [|2.0f;0.0f; 3.0f;5.0f |]; [|1.0f;3.0f; 0.0f;6.0f |]; [|4.0f;5.0f; 6.0f;0.0f |] |]
let example = Array2D.init 4 4 (fun i j -> arrayOfArrays.[i].[j])
// Dynamic programming algorithm
// Subproblems:
// For every destination j in {1,2,......n} every subset S in {1,2,....n} that contains 1 and j, let
// A(S,j) = minimum length of a path of a path from 1 to j that visits precisely the vertices of S [exactly once each]
// create A = Array2D indexed by subsets S that contain 1 and destinations j
// Base Case A[S,1] = 0 if S = {1} , +infinity otherwise
// for m = 2,3,4,.... n (m = subproblem size)
// for each Set S in {1,2,...n} of size m that contains 1
// for each j in S, j different from 1:
// A[S,j] = min (k in S, k <> j) {A[S-{j},k]+Ckj}
// Return min (j=2 to n) A[{1,2,3,....,n},j]+Cj1
let limit = 100000000.0f
//// the first city is city 0 in array D. we put it apart,
//// then we represents S as integers.
//// we take the binary representation of integer, and the pth bit indicates whether city p+1 belongs to S
//// for example S =11 = 1+2+8 contains cities 2,3 and 9 (members 11 will return [(0, 1); (1, 2); (3, 8)])
/////////////////////////////// with path ///////////////////////////////////////////
let TSP_all_c_Dynamic_Programming_with_path_main(D:float32 [,]) = // solves the TSP problem when ALL cities are connected together with an exhaustive search in exponential time O(n^2 2^n)
// the input is a distance matrix in float32
// memory usage is not optimized at all....
let num_cities = Array2D.length1 D
let l2 = Array2D.length2 D
if (l2<>num_cities) then failwith "Distance matrix is not a squared matrix"
let powers_of_2 = [|1;2;4;8;16;32;64;128;256;512;1024;2048;4096;8192;16384;32768;65536;131072;262144;524288;1048576;2097152;4194304;8388608;16777216|]
let power2 k =
if ((k >= 25) || (k<0)) then raise (InnerError("power exponent not allowed"))
else powers_of_2.[k]
let num_subsets = power2 (num_cities-1)
let S_full = num_subsets-1
let A = Array2D.create num_subsets (num_cities-1) (limit,-1y)
A.[0,0]<-(-0.0f,-2y)
let rec sumbits (n:int):int=
let rec helper acc m =
match m with
| 0 -> acc
| 1 -> acc+1 // remove this ?
| _ -> let r = m%2
helper (acc+r) (m>>>1)
helper 0 n
let hashtable = Array2D.create (num_cities-1) num_subsets false // hashtable.[i-1,n]=true if (sumbits n) = i
for k in 1..(num_subsets-1) do hashtable.[(sumbits k)-1,k]<-true
// member returns [(p,2^p);....] if the p+1th city is in S
let members S = [for j in 0..(num_cities-2) do let a= powers_of_2.[j] &&& S
if (a<>0) then yield (j,a)] // list length = num_cities-1
for m in 2..num_cities do // S size m
printfn "m=%A" m
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let mutable count = 1
let Subset_of_size_m = hashtable.[m-2,0..] |> Seq.mapi (fun i x -> (i,x)) |> Seq.filter (fun (a,b)-> (b=true)) |> Seq.map fst |> Seq.toList
for S in Subset_of_size_m do
if m = 2 then let (i,S') = (members S).Head
A.[S',i]<- (D.[0,i+1],-1y) // distance from 0 to city i+1
else
let S'_list = List.fold (fun acc x -> let a = (((snd x)^^^S_full)&&&S) // list of subsets of size m-1
if a = S then acc else (fst x,a)::acc ) [] (members S)
for (j,S') in S'_list do
A.[S,j] <- ([for (k,expk) in (members S') do
yield (fst A.[S',k]+D.[j+1,k+1],k) ]|> List.min |> fun (a,b)->(a,sbyte b))// can do faster than that if we remember from above ?
count <- count + 1 // to check progress in the console
if count%10000 =0 then printfn "count=%A" count
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
// printfn "%A" A
// A.[num_subsets-1,0..]
A
let calculate_path_TSP_all_c_Dynamic_Programming_with_path (D:float32 [,]) =
// calls the core subroutine defined above
let A' = TSP_all_c_Dynamic_Programming_with_path_main D
// memory usage is not optimized at all....
// from here its smooth sailing, just analyzing the results.
let num_cities = Array2D.length1 D
let l2 = Array2D.length2 D
if (l2<>num_cities) then failwith "Distance matrix is not a squared matrix"
let powers_of_2 = [|1;2;4;8;16;32;64;128;256;512;1024;2048;4096;8192;16384;32768;65536;131072;262144;524288;1048576;2097152;4194304;8388608;16777216|]
let power2 k =
if ((k >= 25) || (k<0)) then raise (InnerError("power exponent not allowed"))
else powers_of_2.[k]
let num_subsets = power2 (num_cities-1)
let S_full = num_subsets-1
let A'' = A'.[S_full,0..]
let res' = [for i in 0..num_cities-2 do yield (fst A''.[i]+ example.[0,i+1]) ] |> Seq.mapi (fun i x -> (i, x)) |> Seq.minBy snd
printfn "TSP answer = %A" res'
let path = Array.create (num_cities+1) -1y
let mutable current_city = sbyte (fst res')
path.[num_cities-1]<- current_city// the last city
let mutable current_S = S_full
let mutable next_S = -2
let mutable next_city = -2y
for k in num_cities-2..-1..1 do
next_city <- snd A'.[current_S,int current_city]
next_S <- (S_full^^^powers_of_2.[int current_city]) &&& current_S
//printfn "k=%A current_S=%A next_city=%A" k current_S next_city
path.[k]<-next_city
current_S<-next_S
current_city<-next_city
for i in 0..num_cities do path.[i]<-path.[i]+1y
printfn "path=%A" path
////// main program /////
calculate_path_TSP_all_c_Dynamic_Programming_with_path dist_matrix
旅行推销员问题////////
开放系统
开放系统。集合
open System.Collections.Generic
开放系统
开放系统
打开FSharp.Charting
字符串的内部错误异常
让stopWatch=System.Diagnostics.stopWatch.StartNew()
/////////////////准备数据/////////////////
//文件格式
//[城市数量]
//[x_1][y_1]//坐标
设x=File.ReadAllLines“/Users/francois guillaume.rideau/Documents/MOOC/TSP.txt”
让拆分(文本:字符串)=
text.Split[|'\t''''.''分割
让splitInto2Values(A:string[])=
(浮点A[0],浮点A[1])
让parseLine(line:string)=
线
|>分裂
|>拆分为2个值
设num_cities=int x[0]//25
让cities=x |>Array.tail |>Array.map parseLine/[x|u 1][y|u 1]
让dist_matrix=Array2D.create num_cities num_cities 0.0f
对于0中的i…(num_cities-1)do
对于0中的j…(num_cities-1)do
距离矩阵[i,j]阵列法拉利[i].[j])
//动态规划算法
//子问题:
//对于{1,2,….n}中的每个目标j,{1,2,….n}中包含1和j的每个子集S,让
//A(S,j)=从1到j的路径的最小路径长度,该路径精确访问S的顶点[每个顶点精确访问一次]
//创建A=Array2D,由包含1和目标j的子集S索引
//基本情况A[S,1]=0,如果S={1},否则为+无穷大
//对于m=2,3,4,。。。。n(m=子问题大小)
//对于大小为m的{1,2,…n}中包含1的每个集合S
//对于S中的每个j,j不同于1:
//A[S,j]=min(S中的k,kj){A[S-{j},k]+Ckj}
//返回min(j=2到n)A[{1,2,3,…,n},j]+Cj1
让限值=100000000.0f
////第一个城市是数组D中的城市0。我们把它分开,
////然后我们将S表示为整数。
////我们采用整数的二进制表示,第pth位表示城市p+1是否属于S
////例如,S=11=1+2+8包含城市2、3和9(成员11将返回[(0,1);(1,2);(3,8)])
///////////////////////////////带路径///////////////////////////////////////////
让TSP_all_c_Dynamic_Programming_与_path_main(D:float32[,])=//解决所有城市在指数时间O(n^2^n)内通过穷举搜索连接在一起时的TSP问题
//输入是float32中的距离矩阵
//内存使用根本没有优化。。。。
设num_cities=Array2D.length1d
设l2=Array2D.length2 D
如果是(l2num_城市),则“距离矩阵不是平方矩阵”
让| 1;2;4;8;16;32;64;128;256;512;1024;2048;4096;8192;16384;32768;65536;131072;262144;524288;1048576;2097152;4194304;8388608;16777216 |]
设power2 k=
如果((k>=25)| |(k acc+1//删除此项?
|_u->设r=m%2
助手(acc+r)(m>>>1)
助手0 n
设hashtable=Array2D.create(num_cities-1)num_subsets false//hashtable。[i-1,n]=true如果(sumbits n)=i
对于k in 1..(num_subsets-1)执行哈希表。[(sumbits k)-1,k]Seq.mapi(fun i x->(i,x))|>Seq.filter(fun(a,b)->(b=true))|>Seq.map fst |>Seq.toList