Showing posts with label Problem. Show all posts
Showing posts with label Problem. Show all posts

Sunday, March 13, 2016

Problem 14-2 Josephus permutation

This problem is taken from the book Introduction to Algorithms, Third Edition By Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein:
We define the Josephus problem as follows. Suppose that n people form a circle and that we are given a positive integer m <= n. Beginning with a designated first person, we proceed around the circle, removing every mth person. After each person is removed, counting continues around the circle that remains. This process continues until we have removed all n people. The order in which the people are removed from the circle defines the (n, m)-Josephus permutation of the integers 1,2,...,n. For example, the (7, 3)-Josephus permutation is (3,6,2,7,5,1,4)
a) Suppose that m is a constant. Describe an O(n)-time algorithm that, given integer n, outputs the (n, m)-Josephus permutation.
b) Suppose that m is not a constant. Describe an O(nlg(n))-time algorithm that, given integers n and m, outputs the (n, m)-Josephus permutation.

Answer


a) A simple solution would be using a linked list and delete the mth element until is empty. This runs in Θ(nm+n) = Θ(n).

b) We used an augmented tree for getting the rank of any element in Θ(lg(n)), but instead of implementing a balance tree we build the tree given a range from 1 to n in  Θ(n). So it runs in Θ(n lg(n)+n) = Θ(n lg(n)).

Code of part b) in rust: 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
extern crate itertools;
use itertools::Unfold;
use std::cmp::Ordering;


pub fn permutation(size: u32, m: u32) -> Box<Iterator<Item = u32>> {
    let mut tree = Tree::new(1, size);
    let x = Unfold::new(1, move |a| {
        *a = (*a + m - 2) % tree.size + 1;
        Some(tree.pop_rank(*a))
    });
    Box::new(x.take(size as usize))
}

#[derive(Debug)]
struct Node {
    data: u32,
    left: Tree,
    right: Tree,
}

#[derive(Debug)]
struct Tree {
    size: u32,
    root: Option<Box<Node>>,
}
impl Tree {
    fn new(from: u32, to: u32) -> Tree {
        if from > to {
            return Tree { root: None, size: 0, };
        }
        let mid = from + (to - from) / 2;
        let node = Node {
            data: mid,
            left: Tree::new(from, mid - 1),
            right: Tree::new(mid + 1, to),
        };

        Tree {
            size: 1 + node.left.size + node.right.size,
            root: Some(Box::new(node)),
        }
    }

    fn find_rank(&mut self, rank: u32) -> &mut Tree {
        let r = self.root
                    .as_mut()
                    .expect("rank out of range")
                    .left
                    .size + 1;

        self.size -= 1;
        match rank.cmp(&r) {
            Ordering::Equal => self,
            Ordering::Less => self.as_mut().left.find_rank(rank),
            Ordering::Greater => self.as_mut().right.find_rank(rank - r),
        }
    }

    fn as_mut(&mut self) -> &mut Box<Node> {
        self.root.as_mut().unwrap()
    }

    fn as_ref(&self) -> &Box<Node> {
        self.root.as_ref().unwrap()
    }

    fn empty(&self) -> bool {
        self.root.is_none()
    }
    
    fn delete_node(&mut self) {
        *self = self.root
                    .take()
                    .map(|mut x| {
                        if x.left.empty() {
                            x.right
                        } else if x.right.empty() {
                            x.left
                        } else {
                            x.data = x.right.pop_min();
                            Tree {
                                root: Some(x),
                                size: self.size,
                            }
                        }
                    })
                    .expect("Can't delete None");
    }

    fn pop_min(&mut self) -> u32 {

        if self.empty() {
            panic!("underflow");
        }

        if self.as_ref().left.empty() {
            let data = self.as_ref().data;
            *self = self.root.take().unwrap().right;
            data
        } else {
            self.size -= 1;
            self.as_mut().left.pop_min()
        }
    }

    fn pop_rank(&mut self, rank: u32) -> u32 {
        let ranked = self.find_rank(rank);
        let data = ranked.as_ref().data;
        ranked.delete_node();
        data
    }
}

Friday, September 4, 2015

Problem 12-2 Radix trees



Answer

A radix tree has the following invariants:

1.     L being the set of keys  in the left subtree of F and R being the set of keys in the right subtree of F we have for all F:  ∀ x∈L ∀ y∈R x < y
2.      For all F we have that parent(F) < F

The fist and second invariant holds because of the fist and second 
part of the definition  for lexicographically less than respectively.
Also because of transitivity we know that each key is less than all its descendants.
So knowing this 2 invariant we know we can do a pre-order walk. 
But is pre-oder walk Θ(n)? 

We know that for a tree of x nodes pre-order  walk runs in  Θ(x).
 The key of a node is like a trail to get were he is and each bit is a turn.
In each turn there is a node  but the paths may partially overlap for each key. 


Knowing this we can say that x - 1  ≤ n  and therefore the runtime is  Θ(n)

Sunday, July 19, 2015

Problem 6-2 Analisis of d-ary heaps

6-2 Analysis of d-ary heaps A d-ary heap is like a binary heap, but (with one possible exception) non-leaf nodes have d children instead of 2 children.

a. How would you represent a d-ary heap in an array?

b. What is the height of a d-ary heap of n elements in terms of n and d?

c. Give an efficient implementation of EXTRACT-MAX in a d-ary max-heap. Analyze its running time in terms of d and n.

d. Give an efficient implementation of INSERT in a d-ary max-heap. Analyze its running time in terms of d and n.

e. Give an efficient implementation of INCREASE-KEY(A,i,k), which flags an error if k is less than key at index i.

Answer



a) Being f a floor of the heap, the last index of a floor that is full is:



each index k that is in a floor i is bound by:



Using this bounds we can prove it for any element of floor i. 
 x being the index of a given element of floor i we have:

For the parent we have:



Proof;




b) For this question we can do the same we did for the proof of a binary heap height. Being h the height of the heap we can bound n in terms of h and d:


And so the height is:


d) Extract_max is Max_heapify plus constant time  and Max_heapify for each recursive call needs to find the largest of d child, so the running time is:

c) and d)

Both are the running time of swim plus constant time. So their running time is:


d-ary heap implementation in C++(The root of the heap is zero instead of one):

#pragma once
#include <vector>
#include <iostream>
#include <algorithm>

template<class T, class Func= std::less<T>>
class DHeap
{

public:
    DHeap();
    DHeap(unsigned);
    DHeap(unsigned,const Func&);
    DHeap(const Func&);


    T extractTop();
    void insert(const T&);
    void insert(T&&);
    const T& top();
    T changeKey(unsigned, T&&);
    bool empty() const{ return vec.empty(); }

private:
    std::vector<T> vec;
    Func cmp;
    int D;

    void heapify(unsigned);
    void swim(int);
    unsigned maxIndex(unsigned, unsigned) const;
    unsigned firstChild(unsigned) const;
    int parent(int) const;
    void ensureCapacity();
    unsigned findMax(unsigned first, unsigned to) const;
    template<typename U>
    void genericInsert(U&&);
   
    template< typename E>
    friend std::ostream & operator<<(std::ostream &os, const DHeap<T,E>& p)
    {
        for (const T& d : p.vec)
            os << d << ",";
        return os;
    }
};

template <typename T, typename F >
DHeap<T,F>::DHeap() : DHeap(4){}

template <typename T, typename F >
DHeap<T, F>::DHeap(unsigned d) : DHeap(d, F()){}

template <typename T, typename F >
DHeap<T, F>::DHeap(unsigned d, const F& func):D(d),cmp(func){}

template <typename T, typename F >
DHeap<T, F>::DHeap(const F& func) : DHeap(4, func){}

template <typename T, typename F >
T DHeap<T,F>::extractTop()
{
    if (vec.size() < 1)
        throw new std::underflow_error("underflow");
       
    T max = std::move(vec[0]);
    vec[0] = std::move(vec.back());
    vec.pop_back();
    heapify(0);
   
    return max;
}
template<typename T, typename F>
const T& DHeap<T,F>::top()
{
    return vec[0];
}

template<typename T, typename F>
template<typename U>
void DHeap<T,F>::genericInsert(U&& element)
{
    ensureCapacity();
    vec.push_back(std::forward<U>(element));
    swim(vec.size() - 1);
}

template <typename T, typename F >
void DHeap<T,F>::insert(const T& element)
{
    genericInsert(element);
}

template <typename T, typename F >
void DHeap<T,F>::insert(T&& element)
{
    genericInsert(std::move(element));
}

template <typename T, typename F >
T DHeap<T,F>::changeKey(unsigned index, T&& element)
{
    if (cmp(element, vec[index]))
        throw new std::invalid_argument("invalid argument");

    T old = std::move(vec[index]);
    vec[index] = std::move(element);
    swim(index);

    return old;
}

template <typename T, typename F >
void DHeap<T,F>::heapify(unsigned index)
{
    unsigned first = 0;
    while ((first = firstChild(index)) < vec.size())
    {
        unsigned to = std::min(vec.size(), firstChild(index + 1));
        unsigned largest = findMax(first, to);

        if (!cmp(vec[index], vec[largest])) break;

        using std::swap;
        swap(vec[largest], vec[index]);
        index = largest;
    }
}

template<typename T, typename F>
unsigned DHeap<T,F>::maxIndex(unsigned a, unsigned b) const
{
    return cmp(vec[a], vec[b])? b : a;
}


template <typename T, typename F >
void DHeap<T,F>::swim(int index)
{
    using std::swap;
   
    int p = 0;
    while (index > 0 && cmp(vec[p = parent(index)], vec[index]))
    {
        swap(vec[p], vec[index]);
        index = p;
    }

}
template <typename T, typename F >
unsigned DHeap<T,F>::findMax(unsigned first, unsigned to) const
{
    unsigned largest = first++;
    while (first < to)
        largest = maxIndex(largest, first++);
   
    return largest;
}

template <typename T, typename F >
unsigned DHeap<T,F>::firstChild(unsigned index) const
{
    return D* index + 1;
}
template <typename T, typename F >
int DHeap<T,F>::parent(int index) const
{
    return (index - 1) / D;

}
template <typename T, typename F >
void DHeap<T,F>::ensureCapacity()
{
    if (vec.size() == vec.capacity())
        vec.reserve(3 * vec.size() / 2 + 1);
}
 

Friday, June 5, 2015

Problem 4-6 Monge arrays



Answers 

for a n x m matriz

a.

So we need to proof this:


The right implication is trivial as we can see that the left operand of the if only if is just a especial case of the monge definition. 

So to proof the left implication we assumed


and so we try to proof the monge definition by induction(fist on the rows as the hint suggested):

so we can use our assumption on k :



So using our inductive hypothesis we get:


For this to work l must be l=j+1 but when proving the same thing for the rows we then prove it for all possible k,l,j,i. 

b.

37 23 24 32 
21  6   7  10 
53 34 30 31 
32 13  9   6 
43 21 15  8

c. It can be proved by contradiction.

We make the assumption:  

being A[i,f(i)] and A[i+1,f(i+1)] the leftmost minimums, this must be false:

And so we prove it by contradiction 

d. 

This hold for all the odd rows:

So we just need to find the minimum between those bounds. 

The run time for finding all the leftmost minimums for the odd rows is given by the summation:

e.

Since each time the size is split to just the even rows and the merge step takes O(n+m), the run time
of the algorithm is given by the recurrence:


we analyse it using the recursion tree method and we can ignore the ceiling since it wont affect the asintotic behavior. The base case is O(1) since m is constant so assuming that n is a power of 2 there is lg(n) levels and each one takes m constant time so:




The code for the algorithm is:

C++

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int findMin(int* array, int init, int finale){
    int index = 0;
   
    for (int min = INT_MAX; init < finale; ++init)
    {
        if (min > array[init])
        {
            min = array[init];
            index = init;
        }
    }
    return index;
}

void divideAndConquereMonge(int **matrizMonge, int* leftmost, int rows, int factor, int column){

    if (rows == 1)
        leftmost[0] = findMin(matrizMonge[0], 0, column);
    else
    {
   
        int mid = static_cast<int>(std::ceil(rows / 2.0));
        divideAndConquereMonge(matrizMonge, leftmost, mid, 2 * factor, column);

        for (int i = 1; i < rows - 1; i+= 2)
        {
            int dev = factor * i, end = leftmost[dev + factor] + 1;    
            leftmost[dev] = findMin(matrizMonge[dev], leftmost[dev - factor], end);
        }

        if (rows % 2 == 0)
        {
            int dev = factor * (rows - 1);
            leftmost[dev] = findMin(matrizMonge[dev], leftmost[dev - factor], column);
        }
    }
}

Rust 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
fn left_most_minimum(monge: &[i32], from: usize, to: usize) -> usize {
    let mut min = monge[from];

    (from + 1..to).fold(from, |m,i| {
        if monge[i] < min {
            min = monge[i];
            i
        } else {
            m
        }
    })
}

fn minimums(monge: &[i32], leftmost: &mut Vec<usize>, rows: usize, columns: usize, factor: usize) {
    if rows == 1 {
        leftmost[0] = left_most_minimum(monge, 0, columns) % columns;
    } else {
        let mid = rows - (rows / 2);
        minimums(monge, leftmost, mid, columns, 2 * factor);

        let map = |i, j| (i * columns) + j;

        let x_rows = (0..)
            .map(|x| factor * (2 * x + 1))
            .take_while(|&x| x < (rows - 1));

        for row in x_rows {          
            let (from, to) = (map(row, leftmost[row - factor]),
                              map(row, leftmost[row + factor] + 1));
                             
            leftmost[row] = left_most_minimum(monge,from,to) % columns;
        }

        if rows % 2 == 0 {
            let row = factor * (rows - 1);
            let (from, to) = (map(row, leftmost[row - factor]),
                              map(row, columns));
                               
            leftmost[row] = left_most_minimum(monge, from, to) % columns;
        }
    }
}

fn monge_left_most_minimums(monge: &[i32], rows: usize) -> Option<Vec<usize>> {
    
    let size = monge.len();
    if rows == 0 || size % rows != 0 { return None; }
    
    let mut left_mosts = vec![0; rows];
    minimums(&monge, &mut left_mosts, rows, size / rows, 1);
    Some(left_mosts)
} 

fn main() {
    let row = 7;
    let monge = vec![
        10, 17, 13, 28, 23,
        17, 22, 16, 29, 23,
        24, 28, 22, 34, 24,
        11, 13,  6, 17,  7,
        45, 44, 32, 37, 23,
        36, 33, 19, 21,  6,
        75, 66, 51, 53, 34,
    ];
    println!("{:?}", monge_left_most_minimums(&monge, row));
    
}