27 Haziran 2019 Perşembe

Trie

Giriş
Açıklaması şöyle. Bu veri yapısını telefon rehberini belleğe sığdırmak için kullandım.
A trie, also called digital tree and sometimes radix tree or prefix tree (as they can be searched by prefixes), is a kind of search tree—an ordered tree data structure ...
Radix Tree yazısına bakabilirsiniz

Trie Kullanan Algoritmalar
Aho-Corasick algoritması "string searching" algoritmalarından bir tanesi. Trie kullanılarak çözülebilir.

Avatajları
Şöyledir.
The basics:
 *Predictable O(k) lookup time where k is the size of the key
 *Lookup can take less than k time if it's not there
 *Supports ordered traversal
 *No need for a hash function
 *Deletion is straightforward

New operations:
 *You can quickly look up prefixes of keys, enumerate all entries with a given prefix, etc.

Binary Trie
Trie binary olmak zorunda değil.
Örnek
3 tane kelime içeren (shy,raku,rio) trie şeklen şöyledir.
              ROOT
          /     |  
        s       r    
      /      /     \
     h       a      i
    /      /       /
   y      k       o
         /  
        u
Örnek
Şöyledir
      A
     / \
    B   C
   / \   \
  D   E   F
Trie Tanımlama
Trie tanımlamada önemli olan şey bir node'un kaç tane daha node saklayabileceğine karar vermek. Ayrıca nod'lara ilave bilgiler de eklenebilir. Burada imla düzeltmesi (spell autocorrect) için kullanılan bir Trie var.

Örnek
Şöyle yaparız. Burada Dictionary kullanıldığı için bellek israfı yok
public class Trie : Dictionary<char, Trie>
{
  public void Add(string value)
  {
    var c = String.IsNullOrEmpty(value) ? '\0' : value[0];
    if (!this.ContainsKey(c))
    {
      this[c] = new Trie();
    }
    if (c != '\0')
    {
      this[c].Add(value.Substring(1));
    }
  }
}
Örnek
Basit bir trie için şöyle yaparız
class Trie {

Node root;

public Trie() {
  root = new Node();

}

boolean isPresent(String s) {
    
}
Node'lar ise şöyledir. Burada İngilizce Trie olduğu için 26 düğüm olacağı varsayılmış.
class Node {

  boolean terminal;
  int outDegree;
  Node[] children;

public Node() {
    terminal = false;
    outDegree = 0;
    children = new Node[26];
}
Örnek
Basit bir trie için şöyle yaparız. Önce Letter sınıfını tanımlarız.
//Trie taken from: https://stackoverflow.com/a/6073004
public struct Letter
{
  public const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  public static implicit operator Letter(char c)
  {
    return new Letter() { Index = Chars.IndexOf(c) };
  }
  public int Index;
  public char ToChar()
  {
    return Chars[Index];
  }
  public override string ToString()
  {
    return Chars[Index].ToString();
  }
}
Trie sınıfı şöyledir. Burada alt düğümler için yine Dictionary kullanılmış.
public class Trie
{
  public class Node
  {
    public string Word;
    public bool IsTerminal { get { return Edges.Count == 0 && Word != null; } }
    public Dictionary<Letter, Node> Edges = new Dictionary<Letter, Node>();
  }

  public Node Root = new Node();
  ...
}
Constructor şöyledir.
public Trie(string[] words)
{
  for (int w = 0; w < words.Length; w++)
  {
    var word = words[w];
    var node = Root;
    for (int len = 1; len <= word.Length; len++)
    {
      var letter = word[len - 1];
      Node next;
      if (!node.Edges.TryGetValue(letter, out next))
      {
        next = new Node();

        node.Edges.Add(letter, next);
      }

      if (len == word.Length)
      {
        next.Word = word;
      }

      node = next;
    }
  }
}
Add metodu
Şöyle yaparız.
tring[] arr = ...;
trie.Add(arr);
findByPrefix metodu
Örnek
Şöyle yaparız.
t.findByPrefix("rabb");
Örnek
Şöyle yaparız.
string FindLongestWord(Trie.Node node, string input, int charIndex)
{
  var character = char.ToUpper(input[charIndex]);

  string longestWord = null;

  foreach (var edge in node.Edges)
  {
    if (edge.Key.ToChar() == character)
    {
      var foundWord = edge.Value.Word;

      if (!edge.Value.IsTerminal)
      {
        var longerWord = FindLongestWord(edge.Value, input, charIndex + 1);
        if (longerWord != null) foundWord = longerWord;
      }

      if (foundWord != null && (longestWord == null ||
          edge.Value.Word.Length > longestWord.Length))
      {
        longestWord = foundWord;
      }
    }
  }
  return longestWord;
}
findValidPrefixes metodu
Elimizde şöyle bir Trie olsun.
public class SimpleTrie {
  private static final int ALPHABET_COUNT = 26;

  class TrieNode {
    char value;
    TrieNode[] children;
    boolean isValidWord;

    TrieNode() {
      this(' ');
    }

    TrieNode(char value) {
      this.value = value;
      children = new TrieNode[ALPHABET_COUNT];
      isValidWord = false;
     }
  }

  private TrieNode root = new TrieNode();

  public void insert(String word) {
    TrieNode current = root;

    for (int i = 0; i < word.length(); i++) {
      char c = word.charAt(i);

      if (current.children[c - 'a'] == null) {
        current.children[c - 'a'] = new TrieNode(c);
      }

      current = current.children[c - 'a'];
    }

      current.isValidWord = true;
  }
}
Şöyle yaparız.
public List<String> findValidPrefixes(String word) {
  List<String> prefixes = new ArrayList<>();
  TrieNode current = root;

  StringBuilder traversedPrefix = new StringBuilder();

  for (int i = 0; i < word.length(); i++) {
    char c = word.charAt(i);

    if (current.children[c - 'a'] != null) {
      current = current.children[c - 'a'];
      traversedPrefix.append(c);

      if (current.isValidWord) {
        prefixes.add(traversedPrefix.toString());
      }
    }
  }

  return prefixes;
}
insert metodu
Şöyle yaparız.
Trie<String, String> t = new Trie<String, String>();
t.insert("pizza", "🍕");
t.insert("rabbit1", "🐇");
t.insert("rabbit2", "🐰");

t.findByPrefix("rabb");





24 Haziran 2019 Pazartesi

Ekran ve Concurrency

Giriş
Veritabanı kullanmayan bir çok projede alan bilgisi (domain model) bellekteki nesnelerde saklanır. Bu nesneler karşıdaki sistem tarafından güncellenir. Güncellenen nesneler ile onu gösteren ekran nesneleri arasında bağlantıyı kurmak için izlediğim yöntemler şöyle.

Ekranı Güncelleme Seçenekleri
1. Clone ile Asenkron Güncelleme
Domain nesnesi arka planda çalışan thread tarafından güncellenince, domain nesnesinin bir kopyası alınır (clone) ve ekran gönderilir.

2. Asenkron Güncelleme
Domain nesnesi arka planda çalışan thread tarafından güncellenince, ekrana bir notification gönderilir. Ekran domain nesnesini tekrar okur ve gösterir.

Domain Nesnesi Saklama Seçenekleri

1. Tek Instance İle Çalışma
Domain nesnesi için tek bir instane yaratılır. Bu nesneyi güncellemek veya okumak isteyen lock koyar ve işi bitince kaldırır.

2. Çift Instance İle Çalışma
Write Instance (My Data) ve Read Instance (Original Data) isimli iki kardeş kullanılır. Bu kullanımın amacı ekranda yapılan ancak daha işlenmemiş değişiklikleri farklı ekranlar arasında dolaştırabilmektir.

Read Instance (Original Data) her zaman karşıdan gelen bilgisi saklar. Böylece işleme başlamadan önceki verinin orijinal halini sorgulama imkanı olur. Read Instance güncellenince ekran da güncellenir.

Write Instance en başta karşıdan gelen bilgiyi saklar. Eğer üzerinde değişiklik yapmaya başlarsam benim değişiklik yaptığım bilgiyi saklar. Eğer benim yaptığım değişikik işlenirse Read Instance güncellenir.








Anycast Nedir

Giriş
Anycast bir IP'ye birden fazla sunucunun hizmet vermesidir.

Google DNS
Örneğin Google DNS 8.8.8.8 farklı konumlardaki sunucular tarafından anons edilir. Dünyanın farklı yerlerinden router'lar kendisine en yakın olan kümeyi (dns cluster) tablosuna ekler.
Açıklaması şöyle
A router receiving multiple advertisements for the same network will choose which advertisement to place in its routing table based on the metrics of the routing protocol. This results in the closest (from the perspective of the routing protocol metrics) destination being used by a router.
Faydası Nedir
- Sunucunun cevap verme süresi kısalır.
- Dağıtık bir yapı olduğu için bir kümenin (cluster) erişilemez olması hizmeti kesintiye uğratmaz.

AnyCast Aslında Unicast Gidibir
Açıklaması şöyle.
Anycast is unicast. It is just that the same network is in two different places, and the routing protocol chooses the closest network to which it send traffic destined for that network.
Kendi Router'ım Anycast Kümesini Nasıl Seçer
Açıklaması şöyle.
Anycast is simply that the same network is advertised from multiple places. A router receiving multiple advertisements for the same network will choose which advertisement to place in its routing table based on the metrics of the routing protocol. This results in the closest (from the perspective of the routing protocol metrics) destination being used by a router.

For example, the primary way that BGP determines which of multiple destinations is the closest by the number of ASes in the AS_PATH attribute. If a router receives five advertisements for the 8.8.0.0/16 network, it will choose one to place in its routing table, and it will route the traffic destined for 8.8.8.8 to that network. If the site with that network goes down, the entry in its routing table will be withdrawn, and the next closest destination will be placed in its routing table.

Different routers in different locations will each make their own independent determinations as to which destination is closest. Anycast is simply advertising the same network from multiple sites and letting each router determine for itself where to send traffic destined for that network.




19 Haziran 2019 Çarşamba

Sıkıştırma - Huffman Coding

Giriş
Açıklaması şöyle. Bu yöntem bir çok unix komutunda kullanılmış.
This method is named after D.A. Huffman, who developed the procedure in the 1950s.
Temel olarak iki çeşit Huffman Coding var.
1. Klasik Huffman Coding. Bu yöntem önceden hesaplanmış bir encoding table kullanılır.
2. Adaptive Huffman Coding. Bu yöntemde encoding table çalışma esnasında hesaplanır ve veri ile birlikte gönderilir.

1. Klasik Huffman Coding
Açıklaması şöyle.
To implement Huffman or arithmetic encoding, the compression and uncompression algorithms must agree on the binary codes used to represent each character (or groups of characters). This can be handled in one of two ways. The simplest is to use a predefined encoding table that is always the same, regardless of the information being compressed. More complex schemes use encoding optimized for the particular data being used. This requires that the encoding table be included in the compressed file for use by the uncompression program. Both methods are common.
Eğer encoding table veri ile gönderilecekse veri ilk halinden daha büyük olabilir. Açıklaması şöyle
Huffman coding is a nearly optimal prefix code. It uses shorter bit patterns for more commonly occurring symbols, thus minimizing the amount of space required. Of course, you need to transmit the Huffman tree separately, so Huffman coding does not always compress your data.
2. Adaptive Huffman Coding

Açıklaması şöyle.
Adaptive Huffman coding may be a better fit for the description, as it avoids the need to precompute and share the code tree

3. Arithmetic Encoding
Açıklaması şöyle.
A more sophisticated version of the Huffman approach is called arithmetic encoding. In this scheme, sequences of characters are represented by individual codes, according to their probability of occurrence. This has the advantage of better data compression, say 5-10%. Run-length encoding followed by either Huffman or arithmetic encoding is also a common strategy. As you might expect, these types of algorithms are very complicated, and usually left to data compression specialists.
Kayıp
Huffman kayıpsızdır. Açıklaması şöyle
Huffman code is a particular type of optimal prefix code for characters. It is commonly used for lossless data compression. It is a variable-length code derived from frequency of occurrence.
Prefix Yapısı
Huffman binary tree kullanarak bir prefix code yapısı kurar. Bu binary tree'de en sık görülen elemanlar en üste doğrudur. Böylece bunların kullandıkları bit sayısı azalır. Binary tree'nin kurulması ile ilgili açıklama şöyle
A binary tree will help us to derive our variable-length codes. At each step, we take the two letters with the lowest frequency, merge them under a parent node and assign their total frequency to their parent (a priority queue may help us do that). We will continue doing this until all letters are merged under a single root. 
Prefix Code yapısında hiç bir eleman bir diğerinin kodu ile başlamaz. Yani şöyle bir yapı görülmez.
a=0, b=1, c=10, d=01, e=11
Eğer görülseydi 1001 gelince 1001=cab, 1001=bad ayrımı yapılamazdı.

Huffman Tree oluştururken hangi dalın 0 hangisinin 1 olduğunu bilmek gerekir.

Örnek
Elimizde "do it or not" için bir binary tree olsun
Her karakter için encoding şöyle olur







Sıkıştırma - Delta Compression

Giriş
Açıklaması şöyle.
In science, engineering, and mathematics, the Greek letter delta ()) is used to denote the change in a variable. The term delta encoding, refers to several techniques that store data as the difference between successive samples (or characters), rather than directly storing the samples themselves.
...
Delta encoding can be used for data compression when the values in the original data are smooth, that is, there is typically only a small change between
adjacent values. This is not the case for ASCII text and executable code; however, it is very common when the file represents a signal.
...
The key feature is that the delta encoded signal has a lower amplitude than the original signal. In other words, delta encoding has
increased the probability that each sample's value will be near zero, and decreased the probability that it will be far from zero. This uneven probability
is just the thing that Huffman encoding needs to operate. If the original signal is not changing, or is changing in a straight line, delta encoding will result in
runs of samples having the same value.
Örnek
Şöyle yaparız.
original data stream: 17 19 24 24 24 21 15 10 89 95 96 96 96 95 94 94 95 93 90 87 86 86
delta encoded:        17 2 5 0 0 -3 -6 -5 79 6 1 0 0 -1 -1 0 1 -2 -3 -3 -1 0
Örnek
Elimizde şöyle atarsak giden ikilik sayılar olsun
0000000000
0000000001
0000000010
0000000011
0000000100
...
1111111111
Önce aralarındaki farkı buluruz.
0
1
1
1
1
...
Daha sonra bu farklı run-length encoding ile sıkıştırırız.

Smoothing

Giriş
Dijital Sinyal işlemede gelen sinyal bazen ani zıplamalar gösterir. Bu ani değişimlerden etkilenmemek için smoothing algoritmaları kullanılır. Açıklaması şöyle.
The moving average is the most common filter in DSP, mainly because it is the easiest digital
filter to understand and use. In spite of its simplicity, the moving average filter is optimal for
a common task: reducing random noise while retaining a sharp step response. This makes it the premier filter for time domain encoded signals. However, the moving average is the worst filter for frequency domain encoded signals, with little ability to separate one band of frequencies from another. Relatives of the moving average filter include the Gaussian, Blackman, and multiplepass moving average. These have slightly better performance in the frequency domain, at the expense of increased computation time.
Moving Average Yöntemi
 Açıklaması şöyle.
The moving average of a list is a calculation resulting in a new, smoothed out list, created by averaging small overlapping sublists of the original.
"Running Average" ve "Moving Average" aynı şeylerdir. Moving Average algoritmaları smoothing için en çok kullanılan algoritmalardan bir tanesi. Moving Average algoritmaları şimdiki değeri hesaplamak için geçmiş veriyi kullanır.

1. Simple Moving Average
Elimizde bir liste olsun. Bunu 3'lük alt listelere ayıralım
[8,  4,  6,  2,  2,  4]          Sublists:
                             <-  [8, 4, 6]
                             <-  [4, 6, 2]
                             <-  [6, 2, 2]
                             <-  [2, 2, 4]
Her listenin ortalaması şöyledir.
[6.0, 4.0, 3.3, 2.7]
2. Exponential Moving Average
En çok kullanılan Moving Average algoritmalarından bir tanesi Exponential Moving Average.
Açıklaması şöyle
"exponential moving average" is really just a single-pole lowpass filter

Formül şöyle
a(i+1) = tiny*data(i+1) + (1.0-tiny)*a(i)
Eğer tiny 1 - 1/N olarak seçilirse, window size olarak N seçilmiş gibi olur.

Formül şöyle de yazılabilir.
Sn = αY + (1-α)Sn-1

where Sn is the new average, α is the alpha, Y is the sample, and Sn-1 is the previous average.
Sadeleştirirsek şöyle olur. X yeni
Yn = αX + (1-α)Yn-1 → Yn = Yn-1 + α(X - Yn-1)
Bu da şöyledir. X yeni değer, Y ise eski değerdir.
Y += alpha * (X-Y);