2025年11月3日月曜日

Unihertz Titan2が届いたので初期設定する

 Unihertz Titan2が届く!

クラウドファンディングで出資していたTitan2がやっと届いたので設定していく。



コンテンツ

  1. キーボードの設定
  2. ショートカットキーの設定
  3. Termuxの導入
  4. まとめ

2025年3月23日日曜日

DevTerm A06 設定 – Zotero導入

 DevTermの活用法を考えていたとき、EPUBリーダーとしてのサイズとしてちょうどいいかもしれないので、EPUBリーダーとしてZoteroを導入する。

Zoteroについて

Zoteroはオープンソースの文献管理ソフトウェアで、文献情報の維持管理、ファイルの複数デバイスでの文献ファイル共有等を行える。Windows,Mac,Linux他、iOSデバイス、Androidデバイス(オープンβ)で利用可能となっている。

ただし、EPUBリーダーの機能はモバイルデバイスでは使用できないため、デスクトップ版を利用する必要がある。

Zoteroの使い方等については以下のサイトを参照。


以下導入編

2025年3月20日木曜日

DevTerm A06を購入したので初期設定する

往年のPCを思わせるDevTerm A06を譲ってもらったので、設定していく。
今はラズパイ系のRPI-CM4 Liteがメイン。



2024年8月5日月曜日

WSL2 + Ubuntu 24.04でデスクトップ環境を構築

ことのはじまり

WSL2でLinux GUIを使いたくなった
直接起動するのではなく、リモートデスクトップ経由でデスクトップに接続したい

2024年4月29日月曜日

Termux on Debian(GUI)でibusが落ちる問題の解決策

ことのはじまり

前回の記事でKindleFireHD10(13世代)にUbuntuのGUIを構築したが日本語入力がうまく起動しなかった。
ブラウザも使いたかったし、Ubuntuを捨ててDebianに切り替えることにした。
以下その手順のまとめ。

状況整理(環境と症状)

環境は次の通り
  • KindleFireHD10(13世代)
  • Termux上にUbuntuをインストールし、xfce4でGUIを利用
  • 日本語入力はibusとAquaMozcを利用
発生した症状
  • AquaMozcの設定画面をGUIで開くとどこかをクリックした瞬間落ちる
  • 最初は起動していてくれたibus-daemonが途中から起動しなくなる(手動起動も不可)

日本語環境の設定はこの記事を参考にした。

debianの構築

そもそも論として、ubuntuの入れ方とubuntuが相性が悪いのではと疑い始めprootを経由してdebianを構築することにした。
手順としては以下の記事のubuntuをdebianに読み替えて手順を進めればよい。 debianのインストールが完了したら以下のコマンドを実行して必要なものをインストール
apt-get update && apt-get upgrade -y
apt-get install sudo-y
apt-get install xfce4
apt-get install ibus-mozc

xfce4は前回の記事通り数時間かかるのでしばらく待つ。

日本語環境の設定と問題とソリューション

上記の記事に従って日本語環境設定しても上記の症状は改善しなかった。
そこで、以下の方針で解決することにした
  • ibusを手動で起動する
  • 手動起動に伴いibus-daemonの自動起動設定は削除する
ソリューションとしては以下のコマンドを起動時毎回ターミナルから実行すれば解決する.
ibus start

これでとりあえず安定して日本語入力ができるようにはなる。
しかし結果としてAquaMozcのGUI問題は解決できなかった。
結論としてはとりあず使えるのでヨシッ

2024年3月3日日曜日

Kindle FireHD10(13世代)でUbuntu GUIを使おう

ことのはじまり


FireHD10(13世代)が安かったので買ってみたが使い道がなかったので、 Ubuntu on TermuxでLinuxタブレットにしようとした際の作業を備忘として残す。

2023年12月15日金曜日

JavaScriptで標準入力をPython3っぽく受け取る

Paizaやその他、競プロでJavaScriptを使う時、他の言語と違って面倒なのは標準入力の受取である。 全部一気に受け取ってしまって、それらを配列で管理しなければならない。 この管理が非常に面倒なので、Python3みたいにinput()で一行ごとに受け取りたいと考えることしばしばである。 ふと思ったが、標準入力を一気に受け取って、一行ずつ吐き出すオブジェクトをつくればよいのである。 ということで早速実装
//classは巻き上げられないので最初に書く
class inputClass{
    constructor(){
        this.inputCounter = 0;
        this.list = [];
        this.len = 0
    }
    append (value){
        this.list.push(value);
        this.len++;
    }
    input(){
        if (this.inputCounter < this.list.length){
            return this.list[this.inputCounter++];
        }else{
            return '';
        }
    }
}
let data = new inputClass();
let reader = require("readline").createInterface({
    input: process.stdin,
    output: process.stdout
});
reader.on("line", (line) => {
    data.append(line.split(' '));
})
reader.on("close", () => {
    //読み込み行数を保持するlenプロパティで末尾まで取り出し
    for (let i=0; i < data.len; i++){
        console.log(data.input());
    }
});

入力
1 1
2 2
a
c
f g h

出力
[ '1', '1' ]
[ '2', '2' ]
[ 'a' ]
[ 'c' ]
[ 'f', 'g', 'h' ]

概ね期待通りに実装できた。 イテレーターで実装したほうが便利だと作った後に気づいたが ひとまず実装したのでよしとしたい。

2023年2月17日金曜日

Paizaのレベルアップ問題集『巡回セールスマン問題(近似)』の解説

 Paizaの問題の解説がさっぱりしすぎてて頭を悩ませているのでメモ程度に自分なりの解説を作ってみる。

1. 概要

今回の問題

2-近似によるTSP (paizaランク A 相当)

https://paiza.jp/works/mondai/tsp_problems/tsp_problems__tsp_approx

Python 3で解答に従い書いたコードはこんな感じ

import math

n = int(input())
cities = [list(map(int, input().split())) for _ in range(n)]
routes = []
mst = [[] for _ in range(n)] #隣接行列
tour = []

def calc_dist(a, b):
    return math.sqrt(math.pow(a[0] - b[0], 2.0) + math.pow(a[1] - b[1], 2.0))

class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parent = [i for i in range(n)]
        
    def get_parent(self, a):
        a = self.parent[a]
        while a != self.parent[a]:
            a = self.parent[a]
        return a
    
    def unite(self, a, b):
        a = self.get_parent(a)
        b = self.get_parent(b)
        self.parent[b] = a
    
    def same(self, a, b):
        return self.get_parent(a) == self.get_parent(b)


def dfs(mst, tour, now, before):
    tour.append(now)
    if now != before:
        for nxt in mst[now]:
            if nxt in tour:
                continue
            dfs(mst, tour, nxt, now)
    
for i in range(n):
    for k in range(n):
        tmp = calc_dist(cities[i], cities[k])
        routes.append((tmp, i, k))
        
routes.sort()
uf = UnionFind(n)
for d, i, k in routes:
    if uf.same(i, k):
        continue
    mst[i].append(k)
    mst[k].append(i)
    uf.unite(i, k)

dfs(mst, tour, 0, -1)
print(*tour)


2. 解法

問題を分割して考えていくと答えまでは3ステップ

  1. ユークリッド距離をもとにルートの候補を選定
  2. Union Findをつかって整理
  3. 深さ優先探索でルートを探索
一つ一つ見ていく

2.1 ルートの選定

とりあえず、入力座標の各点から各点までの点と距離を全部計算して、短い順にソートする。短い順にソートすると、最短ルートは、自分から自分に向かう点でユークリッド距離は0になる。
以下の処理がこれに該当する。

for i in range(n):
    for k in range(n):
        tmp = calc_dist(cities[i], cities[k])
        routes.append((tmp, i, k))
        
routes.sort()


2.2 Union Findを使って整理

このままだと、最小全域木はつくれないので、Union Findを使って同じ親を持つルートを排除していく。つまり、(0,0)→(0,0)といったものを消す。また、(0,0)→(0,1)及び(0,0)→(0,2)のうちユークリッド距離の短い方だけを残していく、(0,0) → (0,1)及び(0,1) → (0,0)の一方を消す。そうすることで、mstリストは、最小全域木の隣接リストとなる。

該当部分は以下の通り。

uf = UnionFind(n)
for d, i, k in routes:
    if uf.same(i, k):
        continue
    mst[i].append(k)
    mst[k].append(i)
    uf.unite(i, k)

親が同じならループの先頭に戻り、そうでなければ、隣接リストに情報を記録して、その後に親の情報をufインスタンスに格納していく。

そうすることで親がことなるノードだけの隣接リストが作られる。


3.3 深さ優先探索でルートをたどる。

隣接リストをたどっていく。スタートはどこでもいいが、ここでは0をスタートとしている。

そして出力して終了。


4. まとめ

この問題のポイントは、最小全域木を隣接リストに格納し、それを深さ優先探索でたどっていくとこにあると思う。

参考

公式ブログ
Pythonで「巡回セールスマン問題」を解いてみよう!8つの解法を例題で解説

2022年10月11日火曜日

ワードクラウドを作成する【基本編】

 Colaboratoryで自然言語処理の基本みたいなワードクラウドを生成することができるので使ってみた。

1.そのまま使う(英語のみ)

ワードクライドを作成するライブラリのWordCloudを使うだけならこう。

結果はこんな感じになる。
"the"や"over"が入っていないのは、組み込みのstopwordsに指定されており、画像生成時に削除されているから。
#Colabで使ってDriveに出力する場合
from wordcloud import WordCloud
txt = "The quick brown fox jumps over the lazy dog"
a = WordCloud()
a.generate(text=txt)
#to_fileメソッドで書き出し
a.to_file("/content/drive/MyDrive/tmp/new.png")
wordcloudでそのまま生成したもの
生成例1

stopwordsに"quick"を追加したこのコードでやるとこうなる。
#Colabで実行する場合
#ビルドインのSTOPWORDSをインポートする
from wordcloud import WordCloud, STOPWORDS
txt = "The quick brown fox jumps over the lazy dog"
#brownをstopwordsに加える
stop = set(STOPWORDS)
stop.add("brown")
a = WordCloud(stopwords=stop)
a.generate(text=txt)
a.to_file("/content/drive/MyDrive/tmp/newst.png")
"brown"をstopwordsに加えて生成したもの
生成例2

ワードクラウドでの文字の大きさはword-ranksと出現頻度で決定される。出現頻度への文字の大きさの影響を変更するときはインスタンス作成時にrelative_scalingへ値を与える。
遥かなティペラリー(It's a Long Way to Tipperary)のサビ部分を例にやるとこう。
relative_scaling=0のパラメーターで生成したもの
relative_scaling=0

relative_scaling=0.5のパラメーターで生成したもの
relative_scaling=0.5

relative_scaling=1.0のパラメーターで生成したもの
relative_scaling=1.0

relative_scaling以外で気になったパラメータを公式ドキュメントから抜粋する。
  • height: int (default=200)
    • 画像の縦幅の設定
  • width: int (default=400)
    • 画像の横幅の設定
  • scale: float (default=1)
    • 描写のスケールを設定
  • min_word_length: int, default=0
    • ワードクラウドに含まれるための最低の単語の長さ
  • collocation_threshold: int, default=30 
    • コロケーション(単語と単語のつながり)の判定地
それ以外は公式ドキュメント参照。

2.日本語で使う

wordcloudに日本語辞書は入っていない上に、日本語からワードクラウドを作成するために必要な分かち書きや瀕死の分類などができない。
そこで日本語の形態素解析ツールであるJanomeを使う。
Janomeで簡単に形態素解析を行うには、Tokenizerを使う。日本国憲法前文について、解析を行うとこうなる。
from janome.tokenizer import Tokenizer
txt = """
日本国民は正当に選挙された国会における代表者を通じて行動し、われらとわれらの子孫のために、諸国民と協和による成果と、わが国全土にわたって自由のもたらす恵沢を確保し、政府の行為によって再び戦争の惨禍が起こることのないようにすることを決意し、ここに主権が国民に存することを宣言し、この憲法を確定する。そもそも国政は国民の厳粛な信託によるものであって、その権威は国民に由来し、その権力は国民の代表者がこれを行使し、その福利は国民がこれを享受する。これは人類普遍の原理であり、この憲法は、かかる原理に基づくものである。われらはこれに反する一切の憲法、法令及び詔勅を排除する。
日本国民は、恒久の平和を念願し、人間相互の関係を支配する崇高な理想を深く自覚するのであって、平和を愛する諸国民の公正と信義を信頼して、われらの安全と生存を保持しようと決意した。われらは平和を維持し、専制と隷従、圧迫と偏狭を地上から永遠に除去しようと努めている国際社会において、名誉ある地位を占めたいと思う。われらは全世界の国民が、ひとしく恐怖と欠乏から免れ、平和の内に生存する権利を有することを確認する。
われらは、いずれの国家も、自国のことのみに専念して他国を無視してはならないのであって、政治道徳の法則は、普遍的なものであり、この法則に従うことは、自国の主権を維持し、他国と対等関係に立とうとする各国の責務であると信ずる。
日本国民は、国家の名誉にかけて、全力をあげて崇高な理想と目的を達成することを誓う。
"""
txt = txt.replace('\n', '')
t = Tokenizer()
for token in t.tokenize(txt):
	print(token)

###出力###
"""
日本	名詞,固有名詞,地域,国,*,*,日本,ニッポン,ニッポン
国民	名詞,一般,*,*,*,*,国民,コクミン,コクミン
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
正当	名詞,形容動詞語幹,*,*,*,*,正当,セイトウ,セイトー
に	助詞,副詞化,*,*,*,*,に,ニ,ニ
選挙	名詞,サ変接続,*,*,*,*,選挙,センキョ,センキョ
さ	動詞,自立,*,*,サ変・スル,未然レル接続,する,サ,サ
れ	動詞,接尾,*,*,一段,連用形,れる,レ,レ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
国会	名詞,一般,*,*,*,*,国会,コッカイ,コッカイ
における	助詞,格助詞,連語,*,*,*,における,ニオケル,ニオケル
代表	名詞,サ変接続,*,*,*,*,代表,ダイヒョウ,ダイヒョー
者	名詞,接尾,一般,*,*,*,者,シャ,シャ
を通じて	助詞,格助詞,連語,*,*,*,を通じて,ヲツウジテ,ヲツージテ
行動	名詞,サ変接続,*,*,*,*,行動,コウドウ,コードー
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
、	記号,読点,*,*,*,*,、,、,、
われ	名詞,代名詞,一般,*,*,*,われ,ワレ,ワレ
ら	名詞,接尾,一般,*,*,*,ら,ラ,ラ
と	助詞,並立助詞,*,*,*,*,と,ト,ト
われ	名詞,代名詞,一般,*,*,*,われ,ワレ,ワレ
ら	名詞,接尾,一般,*,*,*,ら,ラ,ラ
"""

単語自体はtoken.surfaceで取り出せるが、これでは、様々な品詞が混じっているうえ、「日本国民」が「日本」と「国民」に分割されていたりしており、このままワードクラウドに用いることはできない。
そこで、Analizerとtokenfilterを用いる。
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import *
txt = """
日本国民は正当に選挙された国会における代表者を通じて行動し、われらとわれらの子孫のために、諸国民と協和による成果と、わが国全土にわたって自由のもたらす恵沢を確保し、政府の行為によって再び戦争の惨禍が起こることのないようにすることを決意し、ここに主権が国民に存することを宣言し、この憲法を確定する。そもそも国政は国民の厳粛な信託によるものであって、その権威は国民に由来し、その権力は国民の代表者がこれを行使し、その福利は国民がこれを享受する。これは人類普遍の原理であり、この憲法は、かかる原理に基づくものである。われらはこれに反する一切の憲法、法令及び詔勅を排除する。
日本国民は、恒久の平和を念願し、人間相互の関係を支配する崇高な理想を深く自覚するのであって、平和を愛する諸国民の公正と信義を信頼して、われらの安全と生存を保持しようと決意した。われらは平和を維持し、専制と隷従、圧迫と偏狭を地上から永遠に除去しようと努めている国際社会において、名誉ある地位を占めたいと思う。われらは全世界の国民が、ひとしく恐怖と欠乏から免れ、平和の内に生存する権利を有することを確認する。
われらは、いずれの国家も、自国のことのみに専念して他国を無視してはならないのであって、政治道徳の法則は、普遍的なものであり、この法則に従うことは、自国の主権を維持し、他国と対等関係に立とうとする各国の責務であると信ずる。
日本国民は、国家の名誉にかけて、全力をあげて崇高な理想と目的を達成することを誓う。
"""
txt = txt.replace('\n', '')
t = Tokenizer()
filters = [CompoundNounFilter(), POSKeepFilter('名詞'), POSStopFilter(['非自立','代名詞'])]
NG = ['非自立', '代名詞'] 
analyzer = Analyzer(tokenizer=t, token_filters=filters)
for token in analyzer.analyze(txt):
  #POSStopFilterがうまく機能しない部分があるので二重にフィルタリングする
  ex = token.extra[0].split(',')
  if all(x not in ex for x in NG):
    print(token)

###出力###
'''
日本国民	名詞,複合,*,*,*,*,日本国民,ニッポンコクミン,ニッポンコクミン
正当	名詞,形容動詞語幹,*,*,*,*,正当,セイトウ,セイトー
選挙	名詞,サ変接続,*,*,*,*,選挙,センキョ,センキョ
国会	名詞,一般,*,*,*,*,国会,コッカイ,コッカイ
代表者	名詞,複合,*,*,*,*,代表者,ダイヒョウシャ,ダイヒョーシャ
行動	名詞,サ変接続,*,*,*,*,行動,コウドウ,コードー
子孫	名詞,一般,*,*,*,*,子孫,シソン,シソン
国民	名詞,一般,*,*,*,*,国民,コクミン,コクミン
協和	名詞,サ変接続,*,*,*,*,協和,キョウワ,キョーワ
成果	名詞,一般,*,*,*,*,成果,セイカ,セイカ
わが国全土	名詞,複合,*,*,*,*,わが国全土,ワガクニゼンド,ワガクニゼンド
自由	名詞,形容動詞語幹,*,*,*,*,自由,ジユウ,ジユー
恵沢	名詞,一般,*,*,*,*,恵沢,ケイタク,ケイタク
確保	名詞,サ変接続,*,*,*,*,確保,カクホ,カクホ
政府	名詞,一般,*,*,*,*,政府,セイフ,セイフ
行為	名詞,サ変接続,*,*,*,*,行為,コウイ,コーイ
戦争	名詞,サ変接続,*,*,*,*,戦争,センソウ,センソー
惨禍	名詞,一般,*,*,*,*,惨禍,サンカ,サンカ
決意	名詞,サ変接続,*,*,*,*,決意,ケツイ,ケツイ
'''
これでワードクラウドに入力する準備は整ったので、あとはjanomeの出力をスペース連結したものをWordCloudに放り込めば完了となる。
ただし、WordCloudには日本語フォントが含まれていないので、フォント指定をすることになる。今回はIPAフォントを利用する。
from pyparsing.helpers import Word
from wordcloud import WordCloud
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import *
txt = """
日本国民は正当に選挙された国会における代表者を通じて行動し、われらとわれらの子孫のために、諸国民と協和による成果と、わが国全土にわたって自由のもたらす恵沢を確保し、政府の行為によって再び戦争の惨禍が起こることのないようにすることを決意し、ここに主権が国民に存することを宣言し、この憲法を確定する。そもそも国政は国民の厳粛な信託によるものであって、その権威は国民に由来し、その権力は国民の代表者がこれを行使し、その福利は国民がこれを享受する。これは人類普遍の原理であり、この憲法は、かかる原理に基づくものである。われらはこれに反する一切の憲法、法令及び詔勅を排除する。
日本国民は、恒久の平和を念願し、人間相互の関係を支配する崇高な理想を深く自覚するのであって、平和を愛する諸国民の公正と信義を信頼して、われらの安全と生存を保持しようと決意した。われらは平和を維持し、専制と隷従、圧迫と偏狭を地上から永遠に除去しようと努めている国際社会において、名誉ある地位を占めたいと思う。われらは全世界の国民が、ひとしく恐怖と欠乏から免れ、平和の内に生存する権利を有することを確認する。
われらは、いずれの国家も、自国のことのみに専念して他国を無視してはならないのであって、政治道徳の法則は、普遍的なものであり、この法則に従うことは、自国の主権を維持し、他国と対等関係に立とうとする各国の責務であると信ずる。
日本国民は、国家の名誉にかけて、全力をあげて崇高な理想と目的を達成することを誓う。
"""
cloud_text = '' #出力用
font_path = '/content/drive/MyDrive/tmp/ipaexm.ttf'
output_path = '/content/drive/MyDrive/tmp/constitution.png'

txt = txt.replace('\n', '')
t = Tokenizer()
filters = [CompoundNounFilter(), POSKeepFilter('名詞'), POSStopFilter(['非自立','代名詞'])]
NG = ['非自立', '代名詞'] 
analyzer = Analyzer(tokenizer=t, token_filters=filters)

for token in analyzer.analyze(txt):
  #POSStopFilterがうまく機能しない部分があるので二重にフィルタリングする
  ex = token.extra[0].split(',')
  if all(x not in ex for x in NG):
  	#スペース区切りで単語を連結
    cloud_text += ' '+ token.surface

wc = WordCloud(font_path = font_path, width=1980, height=1080)
wc.generate(text=cloud_text)
wc.to_file(output_path)

日本国憲法前文で作成したワードクラウド
日本語ワードクラウド生成例


ひとまずこれで日本語のワードクラウドが作成できるようになったが、専門用語や流行語など、janomeのビルドイン辞書に記載されていない単語では上手くいかない。従って、辞書を生成していく必要があるが、それはまた書くことににする。




2022年10月6日木曜日

JavaScriptのPromiseを理解する

 JavaScriptの非同期処理がよくわからない、PromiseってなんぞってなったのでPromiseについてまとめてみた。

1. Promiseは何のため?

A. Promiseは非同期処理のため
これが簡潔な答えになる。
JavaScriptでは、あることをしながら別のことをするということができない。つまり、ウェブサイトのデータをダウンロードしながら画面にスピナーを表示するといったことができない。
これを解決するのが非同期処理になる。JavaScriptでは、Callbackやイベント駆動という方法がある。その新しい方法がPromiseになる。

2. Promiseのコードはどう使うか?

JavaScriptでは.then()という形で呼び出すことになる。

//Fetch APIを使う場合
fetch(url)	//urlにアクセスして
 	.then(response => response.json()) //responseを解釈し
	.then(data => console.log(data));	//データを表示する
(https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetchから引用)

このケースではFetch APIを取得し、対象のURLから情報を受け取り、JSONとして解釈して、そのデータを表示するということを行っている。
言い換えれば、.then()内に与えられる関数(ここではアロー関数)を呼び出して、fetch()の戻り値→response.json()の戻り値を順に与えて処理を行っている。


3. Promiseはどのような仕組みで動くか?

Promiseは、ある関数に一時的にPromiseというものを返し、処理を続行する。このPromiseが満たされるか、失敗するか、解決することでその関数は、処理を解決する。
Promiseを返す関数には2つのコールバック関数を登録することができる。1番目のコールバック関数が呼び出されたときにPromiseは満たされ、2番目のコールバック関数が呼び出されたときにPromiseは失敗する。
つまり、以下のようなコードで2つの関数を登録する。
// funcAは処理を実行、funcBはエラーを処理
fetch(url).then(funcA, funcB) 
//または
fetch(url)
	.then(funcA)
	.catch(funcB)

そして、解決するということは、いわばPromiseを返す関数がPromiseを受け取り、処理がペンディングされている状態になる。そして、あるPromiseがPromise以外の値で満たされたとき、連鎖してPromiseが満たされ、あるPromiseが失敗すれば、同じ理由で連鎖してPromiseが失敗する。
つまり、コードで表すと以下のようになる。
//HTTPリクエストを行い、最初にPromiseを返し、
//リクエストが返ってきたら、Promiseはresponseオブジェクトで満たされる
fetch(url)
//HTTPリクエストのPromiseが満たされるまで
//fetch()から返ってきたPromiseで解決される
//HTTPリクエストのPromiseが満たされると
//response.jsonが返すPromiseは満たされる
    .then(response => response.json())
//response.json()の受け取ったPromiseが満たされるまで
//最初のthen()が返すPromiseで解決される
    .then(data => displayData(data));

4. Promiseの動きを見るためのコード

Promiseは、組み込みのPromiseを返す関数以外にPromiseコンストラクタを使うことでPromiseを返す関数を作成することができる。
これを利用してミリ秒を引数として受け取り、その値を返す関数を書くと次のようになる。
//durationを引数として受け取り
//durationミリ秒後にPromiseをduratoinで満たす
function wait(duration){
    return new Promise((resolve, reject)=>{
        setTimeout(() => {resolve(duration)}, duration);
    })
}


//start→4000→setTimeoutの順でコンソールに表示される 
console.log("Start");
setTimeout(() => {console.log("setTimeout")}, 5000);
wait(4000).then(time => {console.log(time)});

//start→setTimeout→4000の順でコンソールに表示される 
console.log("Start");
setTimeout(() => {console.log("setTimeout")}, 3000);
wait(4000).then(time => {console.log(time)});

5.まとめ

Promiseは仮の値を関数に与えることで処理したことにして(処理したとみなし)、次の処理に進む。そのうえで、最初のPromiseが満たされたとき、連鎖的にPromiseが満たされて結果が返ってくる。

2022年9月28日水曜日

Pythonでヒストグラムを生成する【3/3】

 前回



前回はデータの分類をやったので今回はグラフの作成


3.グラフの作成



こういったグラフにするためには、「階級数」だけリストを作成し、それらの要素数は「最大の階級の値」する。そのうえで、各リストを最後尾から「■」で埋めて、最後に縦軸を合成すればいい。つまり、階級数が10、最大の階級の値が10であれば、10個の空白の要素を持つ10個のリストを用意すればよい。
#sep_listは階級(横軸)
#separatedは元データを階級別に分けたもの

longest = 0
for i in separated:
    if len(i) > longest:
        longest = len(i)
y = longest + 1 
x = len(sep_list)
graph = [[' ']*x for _ in range(y)]
sep_list = list(map(lambda x: (map(lambda y: str(y),x)), sep_list))
 	#sep_listは[int, int]の形なのでstrに変換
graph[y-1] = list(map(lambda x:'~'.join(x), sep_list))
	#グラフのリスト末尾に軸を追加
for i in range(x):
    volume = len(separated[i])
    for k in range(volume,0,-1): #後ろから■で埋めてく
        graph[y-1-k][i] = "■"
    

最後にグラフの縦軸を作成する。
まず、グラフの縦軸の数字を含むリスト用意する。縦軸は、グラフを含むリストの一次元目の要素数~1までをとる。ここでは次のステップの都合上0まで含むリストを「要素数」、「要素数-1」... 「0」といった形で作成する。
その後、このリストをグラフリストの一次元目の要素(リスト)の先頭に入れる形で結合する。
最後にグラフリストの[0][0]を半角スペースに置換する。


#graphはグラフを格納しているリスト
num = [[i] for i in range(len(graph)-1,-1,-1)]
for i in range(len(graph)):
	graph[i] = num[i] + graph[i]
self.graph[len(graph)-1][0] = ' '
最後にこれまでのコードをまとめてクラス化するとこう
class histogram:
    """docstring-
        data_array -> ヒストグラムにしたいデータ(一次元リスト)
        sep -> ヒストグラムの階級    
        drawメソッドで実行
        output_to_csvメソッドでワーキングディレクトリにcsv書き出し
    """
    def __init__(self, data_array, sep):
        self.data = data_array
        self.s = min(data_array)
        self.e = max(data_array)
        self.sep = sep
        self.separated = []
        self.sep_list = []

    def draw(self):
        self.distribute_data()
        self.select_list()
        self.draw_a_histogram()
        self.draw_vertical_axis()

    def draw_a_histogram(self):
        longest = 0
        for i in self.separated:
            if len(i) > longest:
                longest = len(i)
        y = longest + 1
        x = len(self.sep_list)
        self.graph = [[' ']*x for _ in range(y)]
        self.sep_list = list(map(lambda x: (map(lambda y: str(y),x)), self.sep_list))
        self.graph[y-1] = list(map(lambda x:'~'.join(x), self.sep_list))
        for i in range(x):
            volume = len(self.separated[i])
            for k in range(volume,0,-1):
                self.graph[y-1-k][i] = "■"
        
    def distribute_data(self):
        n = (self.e - self.s+1)//self.sep
        if (self.e - self.s +1)%self.sep != 0:
            n += 1
        self.separated = [[] for _ in range(n)]
        for i in range(n):
            if i == 0:
                self.sep_list.append([self.s, self.s + self.sep -1])
            else:
                previous = self.sep_list[i-1][1]
                nxt = previous + self.sep
                if nxt >= self.e:
                    nxt = self.e
                if previous + 1 == nxt:
                    self.sep_list.append([previous+1])
                else:
                    self.sep_list.append([previous+1, nxt])

    def select_list(self):
        for i in self.data:
            counter = (i - self.s) // self.sep
            self.separated[counter].append(i)

    def draw_vertical_axis(self):
        num = [[i] for i in range(len(self.graph)-1,-1,-1)]
        for i in range(len(self.graph)):
            self.graph[i] = num[i] + self.graph[i]
        self.graph[len(self.graph)-1][0] = ' '
        
    def output_to_csv(self):
        import csv
        with open("./histogram.csv",encoding = "utf-8",newline='',mode="w") as f:
            writer = csv.writer(f)
            writer.writerows(self.graph)

2022年9月23日金曜日

Pythonでヒスグラムを生成する【2/3】

 前回

https://beyondthe20th.blogspot.com/2022/09/python13.html


前回は1のステップをやったので今回は2から

2.データの分類

前回の投稿で、横軸の数は、「(最大値 - 最小値+1) / 横軸の幅」で算出した。データを格納すべきリストの数と横軸の数は同じなので、同数のリストを作成する。
    	distributed_list = [[] for _ in range(n)] 
        #nは横軸の数「(最大値 - 最小値+1) / 横軸の幅」 
ここで作成したリストにデータを格納していく。
これは、そのデータが何番目のリストに格納されるかを調べるためには、「(対象のデータ-データリストの最小値)//横軸の幅」で求められる。つまり、10区切り、1~100の場合に、8は、(8-1)//10 = 0となり、0番目のリストに格納される(つまりindex = 0)。
これをPython 3のコードに直すと以下のようになる。
    for i in data_array:
    	counter = (i - start) // sep
        distributed_list[counter].append(i)
これでdistributed_listには次のようにデータが格納された。
distributed_list = [[1番目の軸のデータ], [2番目の軸のデータ]...]
これでデータの分類が完了した。あとは、各配列に格納されているデータの個数をカウントし、縦軸を生成したうえでグラフ化、CSV出力を行えば、作成完了になる。


次回


2022年9月19日月曜日

Pythonでヒストグラムを生成する【1/3】

ヒストグラムは、品質管理の七つ道具として有名で、これを作成するライブラリやソフトウェアは、数多くある。Pythonのライブラリにもmatlabがあるし、自分で作るものでもないのだが、Pythonistaたるものライブラリに頼らず自分で作っていきたいところである。
今回は、ExcelやLibre Office Calcで表示することを前提にCSVでヒストグラムを生成するコードを書いてみた。
出来栄えはこんな感じ、完璧なヒストグラムと言っても過言ではないではないだろう。


さて、これを作るにあたっては、元データを読み込んでから、いくつかステップを踏まねばならない。今回は以下の手順で行った。
  1. 横軸の作成
  2. データの分類
  3. グラフ化
以下では順を追ってみていきたい。

1.横軸の作成

横軸の作成の作成のためには、元データから最初の項目と最後の項目を確認したうえで、いくつのリストが必要かを確認し、横軸のデータを格納するリストを用意する必要がある。従って、以下のような処理が必要になる。
start = min(data_array)
end = max(data_array)
n = (end - start+1)// sep #(最大値-最小値+1)//横軸の幅
if (end - start+1)/sep != 0: 
	n += 1
ここでは、「(最大値 - 最小値+1) / 横軸の幅」の切り上げた値が用意すべきリストの数になることになる。つまり、1~100まで5刻みで横軸を用意したいのであれば、(100-1+1)/5 = 20、つまり20個のリストが必要となり、最後のリストには91~100までが格納される。また、1~101であれば、(101-1+1)/5 = 20.2となり、同様に21個のリストが必要となり、最後のリストには、101が格納される。
次に、軸の値を格納する必要がある。つまり、1~100まで10刻みであれば、1~10、11~20、21~30...といった形で軸の値をリストに格納していく必要がある。これは次のようになる。
#sep_listは軸の値を格納するためのリスト、sepは刻みの値、nは必要なリスト数
for i in range(n):
	if i == 0:
		sep_list.append([start, start + sep -1])
    else:
		previous = sep_list[i-1][1]
		nxt = previous + sep
		if nxt >= end:
			nxt = end
			if previous + 1 == nxt:
				sep_list.append([previous+1])
			else:
				sep_list.append([previous+1, nxt])
これは、前工程で確認したn個のリストの数だけループを回し、ループの最初で「[最小値,最小値+刻み-1,]」、それ以降は、「[前の値+1, 前の値+刻み]」といった形で処理を行っている。つまり、1~100を10刻みで軸とした場合は、最初のループで「[1, 1+10-1]」、二番目のループは「[10+1, 10+10]」という処理になっている。
これで横軸の作成が完了したので、次に収集したデータを分類して、ヒストグラム本体を作成していく。

2/3
3/3

2022年9月10日土曜日

File APIを用いたCSVファイルの入出力

VBAを使いたくないので、HTML+JavaScriptでCSVファイルを処理したいものの、File APIに関する記述がそこら中に散らばっていて、入出力をまとめた記事がないので今回作成してみた。

ファイルオブジェクトの読み書きに関してはPromiseとイベント駆動のものがあるが、今回は、わかりやすいのでイベント駆動のものを採用した。
EventListenerのコールバック関数に引数を与えるのはどうもうまくいかなかったので、グローバルオブジェクトにファイルの内容をロードする形にして読み込み→グローバルオブジェクトで読み書きとした。


    
let loaded_csv = []; //ファイル読み込み用のグローバルオブジェクト
function load(e){
	for (let file of e.target.files){
    	let reader = new FileReader();
        reader.onload = event => {
        	let csv = event.target.result.trim(/\r\n/).split(/\r\n/);
            csv = csv.map( x => x.split(','));
            loaded_csv.push(csv);
            }
            reader.readAsText(file);
        }
}

function create(write_array, filename){
	let csv_string = "";
    for (let i of write_array){
    	csv_string += i.join(',');
        csv_string += '\r\n';
    	}
	let b = new Blob([csv_string], {type: "text/csv"});
    let url_link = URL.createObjectURL(b);
    let link = document.createElement("a");
    link.download = filename;
    link.href = url_link;
    document.body.appendChild(link);
    link.click();
} 

2019年10月20日日曜日

AKの2点スリングを1点スリング風に使用する方法

今回はこのスリングとマルイのノーマルAK-47を使用



1.ストック側だけ普通にスリングを付ける

2.金具の下にスリングを通す形でストックに巻く

3.2回巻き付ける
4.上側で止めて完成

AKのみならずRPKやSVDSでもこの巻き方はできます。ただ、RPKやSVDSは重いので、負荷がかかり壊れる可能性が通常のAKより高いです。

2018年7月15日日曜日

黒い砂漠の闇商人の価格

某ブログに影響されたので私が引いた闇おじの価格を個人メモついでに公開
SSがないため正確な価格が分からないものについては残っていた価格メモを参考にした

2018年3月2日金曜日

東部戦線、第一次世界大戦期戦車の謎

第二次世界大戦の東部戦線において、複数両のMark Ⅴ、Mark Bと思われる車両が確認されている。更にベルリンの戦いにおいて破壊されたMarkⅤ戦車も確認されてる。これらの車両はどのようにしてソ連邦に持ち込まれ、どのような経緯でドイツ軍によって鹵獲されたのか。それを明かにしたい。
MarkⅤ―スモレンスク

Mark BまたはMark C―1941年スモレンスク-1

Mark BまたはMark C―1941年スモレンスク-2

Mark BまたはMark C―1941年スモレンスク-3
Mark BまたはMark C―1941年スモレンスク-4

 Die Deutsche Wochenschau Nr. 570 14:00

1.車輌の入手と戦間期

Mark Ⅴ、Mark Bも双方ともに英国製であり、帝政ロシアの時代にライセンス生産を行っていないことから外部からロシアに持ち込まれた。干渉戦争の際に干渉軍、白軍の支援のため約70両のMark Ⅴが北部、南部の両戦線に持ち込まれ、また、複数両のMark A、Mark B及びMark Cも同時に持ち込まれている。これらの車両は、連合軍、白軍、鹵獲した赤軍によって使用され、戦後には、30年代まで赤軍の教材として使われていた。その後、38年に国防人民委員令によってスモレンスク、ロストフ・ナ・ドヌ、ハリコフ、キエフ、レニングラード、ヴォロシロフグラード、アルハンゲリスクにロシア内戦のモニュメントとして配置されることとなった。
 また、北方に送られた車両のうち、北方のユーデニッヒ軍によって運用されていたMarkⅤの一部は、ホワイトソード作戦失敗後、リトアニアおよびラトビアへと撤退し、同地で両国によって接収された。40年のソ連によるバルト三国併合の際、赤軍がこれらの車両を接収した。


赤軍により鹵獲されたMark B




2.第二次世界大戦

バルバロッサ作戦の開始後、ドイツ軍は、スモレンスクとロストフ、キエフにてMarkⅤを鹵獲している。これらの車両は、写真を見る限りでは大きな破孔や履帯の切断が見られないことから直接戦闘には投入されていないと思われる。このうち、スモレンスクに展示されていた2両(一方の車体番号はNo.9146)は、ベルリンへと移送され、ツオィクハオスに設置された。この二両がベルリンの戦いに投入され、破壊された車両であると思われる。
 また、エストニア方面では、45mm砲を装備したMark Ⅴがソ連軍の撤退を支援するために配置され、戦闘に参加したといわれているが詳細は不明である。


1941年ハリコフ


1から4はどれも同じ場所で撮影されたようだが1は撮影時期が異なる可能性がある。
bundesarchivにさらに一枚
1941年(?)スモレンスク-1
1941年スモレンスク-2
1941年スモレンスク-3

1941年スモレンスク-4



1942年ルガンスク

1942年ルガンスク

ロストフまたはルガンスク

4.第二次世界大戦後

ヴォロシロフグラード、ハリコフ、アルハンゲリスクのMark Ⅴは現在も存在している。一方で、ロストフ・ナ・ドヌの車両は1948年に破棄された。キエフの車両は、破棄されたともドイツ軍によって持ち去られたともいわれており、詳細は判然としていない。レニングラードの車両についても不明である。


5.結論

MarkⅤやMark Bなどの英国製第一次世界大戦型の戦車は干渉戦争を通じてロシアへと持ち込まれ、内戦に参加した。その後の第二次世界大戦では、一部の車両がドイツ軍によって鹵獲されることとなり、ベルリンの戦いでソ連軍に再度鹵獲された。このようにMark Ⅴの数奇な運命が明らかになっている一方で、Mark Bに関する詳細は一切不明である。


エストニア軍のMark Vの写真については以下を参照
https://en.wikipedia.org/wiki/Mark_V_Composite_tank_in_Estonian_service


参考にしたサイト
Estonian Vintage
http://talesanecdotesandtrivia.blogspot.jp/2014/05/estonian-vintage.html

Ужасные и смешные
http://meotyda.ru/node/968

With Lieutenant Colonel Hope Carson in Estonia and Russia(Baltic Defence Review 2/1999)
https://web.archive.org/web/20110927074054/http://www.bdcol.ee/fileadmin/docs/bdreview/11bdr299.pdf

Kharkiv Sights | British Heavy Tank Mark V
https://ukraine-kiev-tour.com/ukraine_kharkiv_sights_british_heavy_tank_mark_v.html

British Tanks of the Inter-war Decades
http://www.alternativefinland.com/british-tanks-inter-war-decades/

Britain’s Forgotten Tank War
https://defenceoftherealm.wordpress.com/2015/02/17/britains-forgotten-tank-war/

White And Allied Tanks In Russia
http://www.minecreek.info/volunteer-army/white-and-allied-tanks-in-russia.html

Did you know that the Ukraine has TWO Mark V tanks??
https://www.warhistoryonline.com/war-articles/know-russia-two-mark-v-tanks.html/3

Фотографии города Харькова, 1941-43 гг. - сделаны немецкими оккупантами - страница клуба _Память_ Воронежского госуниверситета
http://samsv.narod.ru/Klb/City/Harkiv/gal1941.html

Танки на Пушкинской (1920...1942) - Ростов военный _!
http://www.rostovbereg.ru/photo/istorija_rostova/rostov_voennyj/fotografija_1/51-0-331

Charkow 1941
http://wk2.heimat.eu/fotoid44charkow.htm

MK V (WWI) tanks page1
http://beutepanzer.ru/Beutepanzer/uk/MK_V/Mk_V.htm

2017年10月22日日曜日

黒い砂漠:闇商人のアイテム出現

新データ(2017年10月18日アップデート後)

試行回数150 (2017年11月3日時点)

ブラックストーン類:123(82%
ブラックストーン防具束:109
ブラックストーン武器束:14

堅い、尖った:11(7.3%
堅い黒結晶の欠片:2
堅い黒結晶の欠片束:1
先の尖った黒結晶の欠片:5
先の尖った黒結晶の欠片束:3


青武器:8(5.3%
十全武神刀:1
蒼稲妻天聖棍棒:2
蠱惑の呪術デスサイズ:1
アロリアアドソウル:1
改良型バスターガントレ:1
オエキの月輪刀:1
傭兵の鋼鉄大剣:1

クザカ:3(2%
クザカ護符:1
クザカ刀剣:1
クザカアックス:1

ヌーベル:2(1.3%
ヌーベルダガー:1
ヌーベル組み糸飾り:1

ボス防具:1(0.6%
レッドノーズのアーマー:1

金枠アクセ:1(0.6%
オオクジラの奥歯イヤリング(実質青??):1

青枠アクセ:1(0.6%
木の精霊のベルト:1


2017年10月4日水曜日

黒い砂漠:野蛮の亀裂デイリーのスキルポイント増加量

LS Lv57  SP892 プレパケ無し

野蛮前




野蛮後



デイリーと野蛮合わせ経験値2.6%、SP0.1
これは普通に狩ったほうがよい....

2017年9月1日金曜日

黒い砂漠とマイニングについて検証

黒い砂漠って放置ゲーだし放置中にマイニングしたらいいんじゃねって思い検証


検証環境


・スペック
CPU:core i5-3750K
GPU:GTX1050ti OC 4GBモデル
RAM:20GB
OS:Windows10
使用マイニングソフト:MinerGate GUI版 ver6.9

・黒い砂漠
1080p
最適化モード
負荷がかかるものは全てオフ
最小化

・Minergate側の設定
Monero(XMR)
CPU Core:2
GPU Intensity:1


検証結果

マイニングのみ
CPUマイニング:101.78H/s
GPU マイニング:316.45H/s










黒い砂漠のみ
CPUマイニング:73.13
GPU マイニング:314.47









黒い砂漠+ブラウザ「Vivaldi」
CPUマイニング:55.94H/s
GPU マイニング:289.24H/s









結果の値はいずれも直近1分間の平均

ほぼほぼ負荷の度合いの通り。最小化されている間はGPUリソース食わないのかマイニングのみと大きく変わなかった。過去にGTX580 1.5GBを使ったときには、マイニングのみで250H/s、砂漠を入れると50H/sほどだった。Video RAMの量が影響したのかもしれない。
1kw/hの料金を30円程度とした場合、CryptoCompareの計算機を使うと次のようになった。(執筆時のレートで1XMR=$140)



損益計算

マイニングのみ(システム全体の消費電力が200w/hの場合)
$-0.1/d
$-0.9/w
$-3.64/m
$-44.33/y


マイニングのみ(システム全体の消費電力が0w/hの場合)
$1.30/d
$9.11/w
$39.05/m

$475.16/y

黒い砂漠(放置のついでにやるとして消費電力を0w/hとした)
$1.23/d
$8.59/w
$36.81/m
$447.80/y

黒い砂漠+ブラウザ(放置のついでにやるとして消費電力を0w/hとした)
$1.09/d
$7.16/w
$32.63/m
$397.02/y


マイニングのみの場合は高い電気代のため損失が発生するものの、放置ついでと考えれば毎月のプレパケ台くらいは捻出される形になる。計算の際、最大消費電力とアイドル時の電力の差が大きくないことから、放置時のマイニングにかかる電力を0として計算したが、GTX1070などではまた結果は変わってくるかもしれない。また、消費電力を70Wとしてもう一枚GTX1050tiを追加した場合は、1日あたり$0.5の利益が出ることになり、1年程度でマイニングのみで償却できる。リセールバリューを考えた場合、8ヶ月ほどで償却できるのではないか。
結論としては、省電力カードを用いた場合、砂漠とマイニングの相性は抜群ということである。また、MinergateのGUIマイニングソフトは効率が良くないことから、別のマイニングソフトを用いた場合には、より良い結果が得られるだろう。



追記2017年3月4日
マイニングソフトをnice hashに変えた場合
黒い砂漠を最小化しておけば7000~8500satoshi/日ほどの採掘量
黒い砂漠起動中

黒い砂漠最小化中