nemui4の日記: 月を入力すると日数が出るようにするには 26
日記 by
nemui4
上司のおっさんに「月を入力すると日数が出るようにするにはどうすれば?」と聞かれたのでコレを教えてあげた心温まる話
https://togetter.com/li/1279312
パット見まるでわからない・・・(数学疎いな)
こんなの全然知らなかったのは自分だけ?
in my case.
月と日数の配列(table)作って、2月だけ閏年判定関数どっかから拾ってくるか、利用する範囲で対応できる配列作っるかしちゃいそう。
んーと (スコア:1)
例えば、1月と2月だけでよければ、
31 = a * 1 + b
28 = a * 2 + b
を満たすaとbは(a = -3 b = 34)なので、
days(m) = -3 * m + 34
1~3月までだったら、
31 = a + b + c
28 = a * 4 + b * 2 + c
31 = a * 9 + b * 3 + c
を満たすのは(a = 3 b = -12 c = 40)になって、
days(m) = 3 * m**2 - 12 * m + 40
1~4月なら
31 = a + b + c + d
28 = a * 8 + b * 4 + c * 2 + d
31 = a * 27 + b * 9 + c * 3 + d
30 = a * 64 + b * 16 + c * 4 + d
を満たす(以下略)
Re:んーと (スコア:1)
(x-2)(x-3)(x-4)…(x-12)は、xが1の時のみ非0で、2~12の時は0になる。x=1の時は-39916800なので、
f1(x)=(x-2)(x-3)(x-4)…(x-12)*31/-39916800
とすれば、f1(1)=31で、f1(2)~f2(12)=0になる。
同様に、
(x-1)(x-3)(x-4)…(x-12)は、xが2の時のみ非0で、1および3~12の時は0になる。x=2の時は3628800なので、
f2(x)=(x-1)(x-3)(x-4)…(x-12)*28/3628800
とすれば、f2(2)=28で、f2(1)、f2(3)~f2(12)=0になる。
同じようにして f3(x)~f12(x)を求めて、
f(x)=f1(x)+f2(x)+…+f12(x)
とすれば、この関数f(x)は、f(1)=31, f(2)=28, …を満たす。
展開は面倒そうですが、方程式を解かなくても定式化できます。
Re:んーと (スコア:1)
解説サンキューです、おぼろげにわかった気になってきました。
>展開は面倒そうですが、(略)
その面倒そうってとこで既に躓いてました orz.
Re:んーと (スコア:1)
なるほど、解が先にあってそこから数式を導き出す(近似する)んですね。
togetterの式をExcelに打ち込んでみたら2月が27日で4月が29日になった。
数字を打ち込み間違えたかな。
=INT(
-11/907200*A2^11
+163/181440*A2^10
-37/1260*A2^9
+13481/24192*A2^8
-2055371/302400*A2^7
+240683/4320*A2^6
-28268521/90720*A2^5
+85774775/72576*A2^4
-446998571/151200*A2^3
+46351537/10080*A2^2
-221017/56*A2
+1416
)
セル"A2"に月の数字を入れています。
Re:んーと (スコア:2)
切り捨ててるせい。小数で計算する関係で誤差が出ており、例えば 2月は 27.9999999999991 になっている。
svn-init() {
svnadmin create .svnrepo
svn checkout file://$PWD/.svnrepo .
}
Re:んーと (スコア:1)
ありゃ、素でやると四捨五入では丸めてないのか・・・
昔いた事務所の経理の人が、Excelで集計すると誤ってる時があるので必ずデカイ電卓で検算するって言ってたっけ。
Re:んーと (スコア:2)
いや、素じゃなくてINT関数で切り捨ててますよ……。
svn-init() {
svnadmin create .svnrepo
svn checkout file://$PWD/.svnrepo .
}
Re:んーと (スコア:1)
EXCELで四捨五入はROUND関数ですね。
INT関数は指定した数値を超えない最大の整数を返すってやつ。
なので単純な切り捨てでもない。
INT(1.5)→1
ROUNDDOWN(1.5,0)→1
INT(-1.5)→-2
ROUNDDOWN(-1.5,0)→-1
らじゃったのだ
Re:んーと (スコア:1)
てっきりINTが四捨五入でまるめてくれるんだと思ってました。
>EXCELで四捨五入はROUND関数ですね。
>INT関数は指定した数値を超えない最大の整数を返すってやつ。
Excel初心者(じぶん)が躓く罠っぽい(単なる早とちりか)
わはは (スコア:1)
11次近似!
こりゃ一本取られたね。
1〜12までの整数に対して整数値が返さればいいので、11次曲線で完全に計算できますね!
月ごとの日数をテーブルに持つ必要がなくてストレージにも優しいですね(^^)
Re:わはは (スコア:1)
>11次近似!
そんなワードが出てくると脳の血流が停滞します。
2次3次くらいならなんだかわかるけど11次まで跳ね上がると?ってなってしまう非数学脳。
#理系のはしっこにいるつもりなのに数字(学)には弱い・・・
Re:わはは (スコア:2)
要するに、
n+1個の点を通るような関数は、高n次あれば作れる。
そこで、12個の点 (1,31), (2,28), ..., (12,31) を通る11次関数を計算した。
だけといえばだけ。
ただし、自分はパラメータの計算方法が分からないので、導出しろといわれてもできない。
svn-init() {
svnadmin create .svnrepo
svn checkout file://$PWD/.svnrepo .
}
Re:わはは (スコア:2)
×高n次 ○高々n次
svn-init() {
svnadmin create .svnrepo
svn checkout file://$PWD/.svnrepo .
}
Re:わはは (スコア:1)
なんだかんだぐぐってみたら「線型近似」というのを知らない(わかってない)>じぶん
in my case (スコア:1)
以下の条件でやってみる。
ついでに、数式をなるべく短くすることを考える。
(以下では、月はA1セルに格納されているものとする。)
まず、愚直にテーブル引きで実装して、
=INDEX(A1,31,28,31,30,31,30,31,31,30,31,30,31)
月の値はいずれも30日に近いから、30から差分を増減させるように書き換えると、
=30+INDEX(A1,1,-2,1,0,1,0,1,1,0,1,0,1)
これって2月(28日)を別にすると、大の月(31日)と小の月(30日)がほぼ交互に並んでいるように見えて、7月と8月の間だけ順番が飛んでいるように見える。
ということは、月の値に適当な値を掛けてやって、ちょっとだけ早く進んで7月と8月の間で順番が飛ぶようにしてやれば、奇数判定で大の月が得られる。
1/7≒0.143なので、月*1.14がよさそうだ。
つまり、
=30+ISODD(A1*1.14)+2月の補正 ※擬似コード
Excel数式では値の比較の結果はFALSE→0、TRUE→1と変換されることを利用すると、2月は(30-2)日と表せるから、
2月の補正を入れた最終形は、
=30+ISODD(A1*1.14)-(A1=2)*2
となった。
私の頭ではこのあたりが限界のようだ。
Re:in my case (スコア:2)
=VALUE(DATE(2018,A1 + 1,1) - DATE(2018, A1,1))
私がぱっと思いついたのはこんなのでした
Re:in my case (スコア:1)
なるほど、次の月の1日(ついたち)から一日引くんですね。
Excel用だと簡単かつ完璧そう。
#Excelあんまり使ってないので関数も殆ど知らない・・・
Re:in my case (スコア:1)
日付関数ありで、翌月いっぴ-1日の日付で出すなら、
=DAY(DATE(1,A1+1,1)-1)
こんな感じですかね。
Excelが13月を翌年1月とみなす挙動と、「1」を2桁表記の1901年とみなす挙動を利用しているのでトリッキーですが、
A1を年、A2を月とした場合は
=DAY(DATE(A1,A2+1,1)-1)
となるので、まぁまぁ可読性はあるかと。
Re:in my case (スコア:2)
おー確かに私がやったように元月の1日を引くコタ無いですね
-1の日付で行けますね
Re:in my case (スコア:1)
=DAY(((A1+1)&"/1")-1)
とすると年をその時点での年で計算するのでその年がうるう年かどうかで2月の最終日付が変わるようになる。
らじゃったのだ
Re:in my case (スコア:1)
お、こりゃナイス。
ロケールに依存しそうですが、日本内限定なら大丈夫かな。
# でもね、日本でも
# ある日客先から「エクセル(のシートとCSVを使う作業が含まれるソリューション)がおかしくなった!」って呼び出されて、調査したら
# なぜか一台のWindowsで地域設定がオランダに変わっていたとさ
Re:in my case (スコア:2)
これ短くていいですね。
私がExcelでやると愚直にテーブル使ってこんな感じ↓です(A1セルに月が入っている前提)。ちょっと長いですね。
=MOD(QUOTIENT(62648012,(4^A1)),4)+28
Cで書くと意図が明瞭になるのに、Excel関数だとすごくわかりづらい…。
Re:in my case (スコア:1)
ぎょゑ^^;
Excel2013以降ならビット演算関数が使えるので、多少は見通しが良くなります。
BITAND(62648012,3*4^A1)/4^A1+28
というか、その手法を見て気がついてしまった。
Excelは文字列演算ができるんだから、素直に10進整数で行けるじゃんか〜
=MID(323233232303,A1,1)+28
うん、短い。
Re:in my case (スコア:2)
おお、こちらの方が意図が明瞭で解りやすく、かつ短いですね。
Re:in my case (スコア:1)
しまった左右逆でしたorz
=MID(303232332323,A1,1)+28
Re:in my case (スコア:1)
すげー、そうなっちゃうんだ。
わたしの in my case だと、極単純に月と日
#m,d
1,31
2,28
3,31
4,30
...
のテーブルをワークシートの何処かに作っておいて vlookupで引っ掛けようと思ってました。
#これだと無駄が多い