きっかけ
実は今回、自分の使っている商号を法務局に登記して、現在事項証明書を取得して見てみたんですけど、そこに会社法人等番号っていう12桁の番号が書いてあったんですよ。
これ、法人だけだと思ってたんですけどまさか個人の商号登記でもこの番号が振られるとは…
じゃあこれ、あと頭の1桁があれば法人番号を作り出すことができるんじゃないかと思ったんですね。
ただ、法人じゃないので法人みたいに国税庁の法人番号検索サイトで確認っていうのはできないんですよ。
でもなんとなく求めてみたかったんです。
頭の桁の求め方があるのは知ってたので早速確認し、求めてみようって思ったんです。
ちなみに計算方法はこちらを確認して下さい。
ただ、ここで私は思いました。
電卓で求めるのめんどいなぁ…。よし、プログラムに求めさせよう
で、コード書いてみました。
まあ…結果、電卓で求めた方が絶対早かっただろってくらい時間はかかっちゃったんですけどね…
コード紹介
とりまサクっとコード載せちゃいます。
#pragma once
#include <string>
#include <regex>
namespace CalculationImpl {
inline bool AllValueIsHalfNumberText(const std::string& val) {
static const std::regex r(R"([0-9]{12})");
return std::regex_match(val, r);
}
inline bool CheckArg(const std::string& val) {
static const std::regex r(R"([0-90-9]{12,36})");
return std::regex_match(val, r);
}
inline void ConvertNumTextToHalfSizeString(std::string& str) {
std::string Ret{};
for (const char& c : str) {
if (c >= '0' && c <= '9') Ret += c;
else {
if (const unsigned int i = static_cast<unsigned int>(c); i < 0xffffff90 || i > 0xffffff99) continue;
Ret += c - 0xffffff90 + '0';
}
}
str = Ret;
}
inline unsigned long CalcCheckDigit(const std::string& val) {
if (!AllValueIsHalfNumberText(val)) return {};
unsigned int Total = 0;
for (auto i = val.begin(); i < val.end(); i += 2) {
Total += static_cast<unsigned long>(*i - '0') * 2 + static_cast<unsigned long>(*(i + 1) - '0');
}
return 9 - (Total % 9);
}
inline std::string GenerateCompanyNumber(const unsigned long& CheckDigit, const std::string& val) {
if (!AllValueIsHalfNumberText(val) || CheckDigit == 0 || CheckDigit > 9) return {};
return std::to_string(CheckDigit) + "-" + val.substr(0, 4) + "-" + val.substr(4, 4) + "-" + val.substr(8, 4);
}
}
inline std::string CalcCompanyNumber(std::string arg) {
if (!CalculationImpl::CheckArg(arg)) return {};
CalculationImpl::ConvertNumTextToHalfSizeString(arg);
const unsigned long CheckDigit = CalculationImpl::CalcCheckDigit(arg);
if (CheckDigit == 0) return {};
return CalculationImpl::GenerateCompanyNumber(CheckDigit, arg);
}
実はこの前世のコードってのもあるんですよ。
こんな感じです。
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <regex>
#include <utility>
#include <algorithm>
namespace CalculationImpl {
namespace {
void ReplaceString(std::string& src, const std::string& oldStr, const std::string& newStr) noexcept {
std::string::size_type Pos(src.find(oldStr));
while( Pos != std::string::npos ) {
src.replace(Pos, oldStr.length(), newStr);
Pos = src.find(oldStr, Pos + newStr.length());
}
}
}
inline bool CheckArg(const std::string& val) {
static const std::regex r(R"([0-90-9]{12,36})");
return std::regex_match(val, r);
}
inline void ConvertNumTextToHalfSizeString(std::string& str) {
ReplaceString(str, "0", "0");
ReplaceString(str, "1", "1");
ReplaceString(str, "2", "2");
ReplaceString(str, "3", "3");
ReplaceString(str, "4", "4");
ReplaceString(str, "5", "5");
ReplaceString(str, "6", "6");
ReplaceString(str, "7", "7");
ReplaceString(str, "8", "8");
ReplaceString(str, "9", "9");
}
inline std::vector<unsigned long> SplitAll(const std::string& str) {
static const std::regex r(R"([0-9]{12})");
if (!std::regex_match(str, r)) return {};
std::vector<unsigned long> Ret{};
for (const auto& i : str) Ret.emplace_back(static_cast<unsigned long>(i - '0'));
return Ret;
}
inline unsigned long CalcCheckDigit(const std::vector<unsigned long>& arr) {
if (arr.size() != 12 || std::any_of(arr.begin(), arr.end(), [](const unsigned long& v) { return v > 9; })) return 0;
bool b = false;
unsigned long TotalBuf[2] = { 0, 0 };
for (const unsigned long& i : arr) TotalBuf[std::exchange(b, !b)] += i;
return 9 - ((TotalBuf[0] * 2 + TotalBuf[1]) % 9);
}
}
実行速度
実は前世の方のコードを@yumetodoちゃんに見せたら、チェックディジット計算部をがSIMD使って書き直したいって言い、書いたみたいなんですね。
https://qiita.com/yumetodo/items/6a6520536bf54531e48d
で、そのコードと私のコードの実行時間を比較してみたんですよ。
結果、こんな感じでした
番号のパターン | 私(合計) | 私(平均) | yumetodoちゃん(合計) | yumetodoちゃん(平均) |
---|---|---|---|---|
700110005901 | 21,054,500 | 2,105 | 1,524,100 | 152 |
700110005901 | 18,090,800 | 1,809 | 1,560,000 | 156 |
700110005901 | 18,794,100 | 1,879 | 1,524,300 | 152 |
※単位:ナノ秒
コードの削除
私のコード、iutestでUnit Testするためにコードを名前無しnamespaceに入れてないだけなので、正直名前なしnamespaceにCalculationImplの中のコードって外部から隠蔽できるんですよ。で、隠蔽することで正規表現による文字列検査を無くせるので
- CalcCheckDigit
- GenerateCompanyNumber
の2個の関数でやってる正規表現による文字列検査は不要になるんですよ。
だからまずそれを削除することは検討はしてるんですけどUnit Testができなくなるのがなぁ…
まあそのへんはもう少し考えてみて、考えがまとまったら追記しようかなって考えています。
結論
1個求めるだけなら電卓使え