# 問題解決力を鍛える!アルゴリズムとデータ構造
## 第10.5章~
- 任意参加
- ファシリテーター: tsujita
### 話す・書く順番決めルーレット
in Ruby
```ruby
['hsu', 'chen', 'teruya', 'kanazawa', 'okura', 'tsujita'].shuffle.each { |v| puts ">[name=#{v}]\n\n" }
```
in JavaScript
```javascript
const l = ['hsu', 'chen', 'teruya', 'kanazawa', 'okura', 'tsujita']
l[Math.floor(Math.random()*l.length)]
```
### ファシリテーター決め方
- `%w(hsu chen teruya kanazawa okura kanno)`の順番でやっていく
### 参加者がやること
- 事前に決められた章を読む
- 学んだことやわからなかったことを書き込む(任意)
当日
- 最初に感想や質問を書く時間を設ける(10分)
- 時間枠 60分
- 延長枠 30分
### 勉強会時にやること
各自学びや気付き、疑問を発表していく
ファシリテーターがよしなに議論を進める
終了前に勉強会を振り返る?(KPTなどで)← 60分のときに仮締め
## 各自が書き込む場所
>[name=tsujita]
- ヒープの部分で高校数学の数列を思い出しました・・・
>[name=teruya]
https://atcoder.jp/contests/past202004-open/tasks/past202004_f
```ruby=
# 最大が最上位の親になる 優先度付きキュー PQ
# 最小が最上位の親にする時は、コメントアウト箇所を変更
# 更新機能
class PriorityQueue
attr_accessor :data, :size
def initialize(array = [])
@data = []
array.each { |a| push(a) }
end
def push(element)
current = @data.size
@data[up_heap(current, element)] = element
end
def pop
ret = @data.first
x = @data.last
@data[down_heap(0, x)] = x
@data.pop
ret
end
def update(current, element)
index = up_heap(current, element)
index = down_heap(current, element) if index == current
@data[index] = element
end
def size
@data.size
end
def front
@data.first
end
def empty?
@data.empty?
end
def parent(num)
(num - 1) / 2
end
def left_child_index(num)
2 * num + 1
end
def right_child_index(num)
2 * num + 2
end
def have_parent?(index)
index > 0
end
def have_child?(index)
left_child_index(index) < @data.size
end
private
def up_heap(current, element)
while have_parent?(current)
p_index = parent(current)
break if @data[p_index] >= element
# break if @data[p_index] <= element
@data[current] = @data[p_index]
current = p_index
end
current
end
def down_heap(current, element)
while have_child?(current)
a_index = left_child_index(current)
b_index = right_child_index(current)
a_index = b_index if @data[b_index] && @data[b_index] > @data[a_index]
break if @data[a_index] <= element
# a_index = b_index if @data[b_index] && @data[b_index] < @data[a_index]
# break if @data[a_index] >= element
@data[current] = @data[a_index]
current = a_index
end
current
end
end
n = gets.to_i
# 初期値がarrayのhash
tasks = Hash.new { |hash, key| hash[key] = [] }
n.times do
day, point = gets.split.map(&:to_i)
tasks[day] << point
end
# pp tasks
# {1=>[3], 2=>[2, 4]}
pq = PriorityQueue.new
sum = 0
(1..n).each do |i|
tasks[i].each { |point| pq.push(point) }
sum += pq.pop
puts sum
end
```
>[name=okura]
### 10.5 木
よく見かけるのは根付き木な気がする。
### 10.6 順序木と二分木
順序木という概念は初めて知った。二分木はよく聞く。強平衡二分木の性質が良いので、なんとかしてデータ構造をそれにしたい(そのためには多少コストをかけてもよい)という感じなのだろうと思った。
### 10.7 ヒープ
ヒープはスタックと対になる感じで「Rubyのしくみ」とかで見かけたことがある。
ヒープはRubyだと何が使われるんだろうと思って調べたけど、あんまり出てこない。
https://github.com/florian/rb_heap
### 10.8 二分探索木
Gemとかはなさそうだけど、自前で割と簡単に書けるのかな。
### おまけ
`RubyVM::AbstractSyntaxTree`に追加したメソッド。
```ruby=
def traverse(*types, &block)
if block_given?
children.each do |node|
yield(node) if node.respond_to?(:type) && (types.empty? || types.include?(node.type))
node.traverse(*types, &block) if node.respond_to?(:traverse)
end
else
to_enum(:traverse, *types)
end
end
def parent_of?(other)
children.map(&:node_id).include?(other.node_id)
end
```
>[name=chen]
- ヒープは、アルゴリズムとして大体理解できるけど、どこで使われるのがよく分かりないんですね...
- code 10.5 ヒープの実装例、jsで書きました。
```javascript=
const heap = []
let size = heap.length
function heap_push(x){
heap.push(x)
let i = size - 1
while(i > 0) {
let p = (i - 1) / 2
if(heap[p] >= x) break
heap[i] = heap[p]
i = p
}
heap[i] = x
}
function get_top(){
if(heap.length > 0) return heap[0]
else return -1
}
function heap_pop(){
let size = heap.length
if(heap.length === 0) return
let x = heap[heap.length - 1]
heap.pop()
let i = 0
while( i * 2 + 1 < size){
let child_1 = i*2 + 1
let child_2 = i*2 + 2
if(child_2 < heap.length
&& heap[child_2] > heap(child_1))
{
child_1 = child_2
}
if(heap[child_1] <= x) break
i = child_1
}
heap[i] = x
}
heap_push(5)
heap_push(6)
heap_push(1)
console.log("heap : " + heap)
console.log("top : " + get_top())
heap_pop()
console.log("heap : " + heap)
console.log("top : " + get_top())
heap_push(11)
console.log("heap : " + heap)
console.log("top : " + get_top())
// "heap : 5,6,1"
// "top : 5"
// "heap : 5,1"
// "top : 5"
// "heap : 5,1,11"
// "top : 5"
```
## 振り返り
>[name=tsujita]
せっかく勉強しているので仕事でアルゴリズムを活かしていきたい・・
> [name=teruya]
優先度付きキューでないと対応不可能な課題は少ないと思いますね🧐
>[name=okura]
"heap"
> 積み重ね,堆積(たいせき),山(積み)
> [name=chen]
ヒープの利用できる場合
https://ylb.jp/2006b/proc/heap/
> プログラムが使うメモリ領域として、プログラムの機械語が置かれている領域(プログラム領域、またはテキスト領域)のほかに、静的領域とスタック領域があることはすでに学びました。 この章では、もう1つのメモリ領域であるヒープ領域(heap)について学びます。 ヒープを使うと「必要になった時に必要なぶんだけメモリを確保する」ことができます。
うんん...大学卒業から今まで全く使ったことがないっすね...:thinking_face:
## 終了後ファシリテーターがやること
- 次回のファシリテーターを決める
- hsuさんに確認する
- 読む範囲を決める
- 第11章