Proportion of self-avoiding walks on a square lattice

Haskell, 153 149 144 140 126 118 92 bytes

Here we represent the 2d coordinates as a single integer: We start at 0, and the directions N,E,S,W correspond to adding +n,+1,-n,-1 where n is the input (we could also use any larger number). Using this we generate all possible paths, and then just check for duplicate numbers in those paths.

Thanks @H.PWiz for -26 bytes!

g n|a<-scanl(+)0<$>mapM(\_->[1,-1,n,-n])[1..n]=sum[1|x<-a,[b|b<-x,c<-x,b==c]==x]/sum[1|x<-a]

Try it online!

Explanation

--generate all possible paths
    a<-scanl(+)0<$>mapM(\_->[1,-1,n,-n])[1..n]
--count the non-self intersecting paths, compute the ratio to the total
g n|a<- ... =sum[1|x<-a,[b|b<-x,c<-x,b==c]==x]/sum[1|x<-a]

Python 2, 89 bytes

f=lambda n,S=[0]:n>=len(S)and sum(f(n,S+[S[-1]+d])for d in[-n,-1,1,n])/4.or len(set(S))>n

Try it online!

A recursive approach inspired by flawr's lovely Haskell answer. Outputs a float.


Jelly, 15 12 bytes

‘ı×Ƭ¤ṗÄQƑ€Æm

Try it online!

A monadic link taking N as its argument and returning a float representing the proportion of non-self-intersecting walks of length N. Generates all walks of that length and then checks for intersections, using complex numbers to represent the 2D coordinates.

Thanks to @LuisMendo for saving a byte, and @Mr.XCoder for saving 2 more!

Explanation

‘            | Increment by 1
    ¤        | Following as a nilad
 ı           | - 1i
  ×Ƭ         | - Multiply (by 1i) until no new values, returning all intermediate values
     ṗ       | Cartesian power (with N+1)
      Ä      | Cumulative sums of innermost lists
       QƑ€   | Check whether each list is invariant when uniquified
          Æm | Arithmetic mean