воскресенье, 11 декабря 2016 г.

Intervals merge

Two simple algorithms of interval merge, which I designed yesterday while was sick :)

First (and simplest) algorithm

First, is very dumb and is based on check of edges projection: if projected edge (node) is contained in another interval then we have solid interval as result (we must merge them), elsewhere - keep original intervals, see picture:

x1 x2 x3 x4

Python implementation:

# predicat: `x` is contained in (x0, x1)
c = lambda x, x0, x1: x0 <= x <= x1

# merge-I
def m1(i1, i2):
    i1, i2 = map(isort, (i1,i2))
    sx = isort(i1 + i2)
    x1, x2 = i1; x3, x4 = i2
    if c(x1, x3, x4) or c(x3, x1, x2) or c(x2, x3, x4) or c(x4, x1, x2):
        return (sx[0], sx[-1])
    else:
        return ((sx[0], sx[1]), (sx[2], sx[3]))

where isort() is sort function, which returns tuple instead of list:

# sort interval `i`
isort = lambda i: tuple(sorted(i))

Don't forget that first is sort of intervals (normalizing) and then sort of all points - see sx.

Second algorithm

Second is more interesting. It is based on gap detection: we try to find - is there gap between intervals? If it exists then we return 2 intervals as is, otherwise - merge them into one by get (first, last) of sorted points.

x1 x2 x3 x4 dx x1 x2 x3 x4 dx x3 x4 x1 x2 dx dx < 0 x3 x4 x1 x2 dx > 0 dx < 0 dx = 0

where dx is difference between 2nd point of 1st interval - 1st point of 2nd interval (intervals are sorted, so "1st" and "2nd" means index in order), i.e.

Dx = I12 - I21

Xs = {X1 ≼ X2 ≼ X3 ≼ X4}
Is = {I1 ≼ I2}

I12 - I21 ≥ 0 =>  RESULT := [X1, X4]
I12 - I21 < 0     RESULT := [[X1, X2], [X3, X4]]

where I is interval (pair of 2 X points).

# Tuple 0st item selector
p1 = lambda p: p[0]

# merge-II
def m2(i1, i2):
    i1, i2 = map(isort, (i1,i2))
    sx = isort(i1 + i2)
    si = sorted((i1, i2), key=p1)
    if si[0][1] - si[1][0] >= 0:
        return (sx[0], sx[-1])
    else:
        return ((sx[0], sx[1]), (sx[2], sx[3]))

Test it:

# assert
def ass(a, b):
    if a != b: raise AssertionError('%r != %r' % (a,b))

# asserts algo. m1
ass(((1,2), (10,20)), m1((1,2), (10,20)))
ass(((1,2), (10,20)), m1((2,1), (20,10)))
ass((1,20), m1((2,1), (2,20)))
ass((1,20), m1((10,1), (2,20)))
ass((1,20), m1((20,1), (5,20)))
ass((1,20), m1((20,1), (5,10)))
ass((1,20), m1((20,1), (1,10)))
ass((-1,20), m1((20,1), (-1,10)))
ass((-1,20), m1((20,1), (-1,1)))
ass(((-1,0), (1,20)), m1((20,1), (-1,0)))
ass((1,20), m1((2,10), (20,1)))

ass(((1,2), (10,20)), m2((1,2), (10,20)))
ass(((1,2), (10,20)), m2((2,1), (20,10)))
ass((1,20), m2((2,1), (2,20)))
ass((1,20), m2((10,1), (2,20)))
ass((1,20), m2((20,1), (5,20)))
ass((1,20), m2((20,1), (5,10)))
ass((1,20), m2((20,1), (1,10)))
ass((-1,20), m2((20,1), (-1,10)))
ass((-1,20), m2((20,1), (-1,1)))
ass(((-1,0), (1,20)), m2((20,1), (-1,0)))
ass((1,20), m2((2,10), (20,1)))

print 'ok.'

These algorithms are based on metrics: if we can map nodes to natural numbers, we can apply algorithms to intervals of such nodes, For example, nodes can be cities, and metric will be distance from some center, e.g. capital. This means that it's easy to merge path fragments where path nodes are cities. You can imagine any other case sutisfied such condition.

пятница, 21 октября 2016 г.

Expressions notations

What kind of notations for expression syntax do you know? Basincs are:

  • INFIX: 5 * 2 + 3 = 13 (implicit precedence)
  • POSTFIX: 5 2 * 3 + (stack based, explicit precedence)
  • PREFIX: + 3 * 5 2 (reversed postfix)
  • NESTED: (+ (* 5 2) 3) (lisp/s-expressions)
  • FUNCTIONAL: +( *(5 2) 3) (m-expression)
  • MATCHFIX: (* (+ 5 2 +) 3 *) (remembed "end/end if/fi"?)

Another one?

Some of them are very easy for implementation and parsing is trivial (prefix/postfix/nested/matchfix), but more human readable is infix which leads to creation of parsing theory.

Original

суббота, 8 октября 2016 г.

Integers to intervals (Haskell solution)

This is the known task ("problem" from Clojure problems list): to convert integers list to intervals by merging they... See:

[1,2,3,5,7,6,10] -> [<1,3>,<5,7>,10]

First, I create type El which is input list element and can be point or already interval (with simple factory iv). Relation Rel is needed to determine relative location of point and interval. Join of point to interval is based on this result. joinToIv joins point to interval and returns possible new one. Function joinToIvs joins point to one of intervals but if it is not possible then returns original intervals with new one added. Main algorithm is a left folding of joinToIvs.

Code is very dirty and sure may be more short :-)

-- Dirty code of turn integers to intervals
module Intervals where

-- element is interval Iv of 2 integers or point of 1 integer
data El = Iv Integer Integer|Pt Integer
instance Show El where
  show (Iv n m)|n == m = show n
               |otherwise = "<" ++ show n ++ "," ++ show m ++ ">"
  show (Pt n) = show n

-- constructor for intervals from 2 integers
iv :: Integer -> Integer -> El
iv n m|n < m     = Iv n m
      |otherwise = Iv m n

-- relation pt/interval: L (left), LS (left-side), IN (included), RS (right-side), R (right)
data Rel = L|LS|IN|RS|R deriving Show

-- relation
rel :: El -> El -> Rel
rel (Iv a b) (Pt n)  |a - n == 1  = LS
                     |a - n > 1   = L
                     |b - n >= 0  = IN
                     |b - n == -1 = RS
                     |otherwise   = R
rel (Iv a b) (Iv c d)|c - b == 1 = RS
                     |otherwise  = R

-- join integer to element (which is expected to be interval)
-- joinToIv :: El -> Integer -> [El]
joinToIv :: El -> Integer -> Maybe El
joinToIv e@(Iv a b) n = let p = Pt n in
  case rel e p of
    LS -> Just $ iv n b
    IN -> Just e
    RS -> Just $ iv a n
    otherwise -> Nothing

-- join integer to list of intervals by extend some or add new one to the end
joinToIvs :: [El] -> Integer -> [El]
joinToIvs [] n = [iv n n]
joinToIvs (e:es) n =
  maybe ([e] ++ joinToIvs es n) (\e' -> _add e' es) (joinToIv e n) where
  _add (Iv a b) ((Iv c d):es1)|abs (c-b) == 1 = [Iv a d] ++ es1
  _add e1 es1 = [e1] ++ es1

-- Turn integers to intervals
toIv :: [Integer] -> [El]
toIv [] = []
toIv (n:ns) =
  foldl (joinToIvs) [iv n n] ns

--------------------------------------------------
main :: IO ()
main = print l >> print (toIv l) where
  l = [1,2,3,5,6,4,0,10,11,9,12,100,-1,80,99,101,98,8,7]

воскресенье, 28 августа 2016 г.

AWK Literate Programming Personal Tool

LAWK is the simple literate programming tool for personal usage (no collaboration features). For collaboration nanolp can be used see.

Input files are markdown, output - any one. Supports non-ASCII, Windows. Does not support spaces and other special chars in paths. Source is here.

Good feautures are:

  • very clean syntax of definition, pasting, writing named chunks
  • simple dependencies (awk, mkdir, rm, make) - available for Windows too
  • no installation (only copy source somewhere)
  • markdown input format
  • no special syntax for code chunks
  • ctags tag file generation (unsorted)