忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2024/04/27 05:19 |
Windows7にVagrantをインストール
その1 / その2 / その3

自分用の備忘録。
http://www1205uf.sakura.ne.jp/wp/world/?p=398
http://d.hatena.ne.jp/naoya/20130205/1360062070
このあたりを真似ただけです。

Rubyをインストール

http://rubyinstaller.org/
DownloadからRuby1.9とDEVELOPMENT KITをダウンロード。
『Ruby 1.9.3-p545』『DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe』だった。
2.0でもいいかもしれませんがRubyはよくわからんので前述のサイトに従った。

Rubyをインストール。
「rubyのパスを通す」にチェックを入れた。

DevKit-tdm-32-4.5.2-20111229-1559-sfx.exeを適当な場所に解凍。
コマンドプロンプトから
> cd 解凍したディレクトリ
> ruby dk.rb init
> ruby dk.rb install

なお、なにをしたかの意味はわかっていない。

VirtualBoxをインストール

https://www.virtualbox.org/
DownloadからVirtualBoxをダウンロード。
『VirtualBox 4.3.12 for Windows hosts  x86/amd64』だった。
インストール時に、インストールしたディレクトリを記録しておく。

ユーザー環境変数PathにさきほどのVirtualBoxのパスを追加。
変更していない場合『C:\Program Files\Oracle\VirtualBox』だった。

Vagrantをインストール

http://www.vagrantup.com/
DOWNLOADからWindows版のLatestをダウンロード。
『vagrant_1.6.3.msi』だった。
そのままインストール。
ここでWindowsの再起動があった。

VirtualBoxのvmをインストール。

VirtualBoxはただの空HDDなので、中にOSを入れないと動かない。
http://www.vagrantbox.es/
から好きなOSを選択。
今回はCentOSの最新版、『CentOS 6.5 x86_64 (x86_64-20140116.box)』を選択。
種類がありすぎて何が何だかわからぬ。

コマンドプロンプトから
> cd C:\path\to\vagranfiledir\centos65
> vagrant box add centos_65 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box
> vagrant init centos_65

vagrant box addで対象のvmをダウンロードしてくる。
ユーザディレクトリ配下の.vagrant.d\boxes以下に保存されるようだ。
vagrant initで設定ファイルVagrantfileが作成されるのだが、何故かカレントディレクトリにできる。
複数の環境で設定を分けたい場合など、Vagrantfileをディレクトリ別に分けとないといけないらしい。
もしかしたら違う設定とかあるかもしれないが、よくわからない上にこれで動いたのでよしとする。

Vagrantfileを編集

    config.vm.network "private_network", ip: "192.168.33.10"
    config.vm.network "forwarded_port", guest: 5901, host: 15901
    config.vm.provider "virtualbox" do |vb|
      vb.gui = true
      vb.customize ["modifyvm", :id, "--memory", "1024"]
    end

のコメントを外す。
forwarded_portは値も変更する。
値に何らかの意図があるのかは不明。たぶん適当。
http://qiita.com/snaka/items/4fc6f83022e01daea58a
192.168.33.10はデフォルトのまま。どうしてこのアドレスかは不明。

なお
    config.vm.network "public_network"
を有効にすると外部から見える状態になるので注意。

Vagrantを起動

> vagrant up

This kernel requires an x86-64 CPU, but only detected an i686 CPU.
Unable to boot - please use a kernel appropriate for your CPU.

あふん。
32ビットCPUなので、x86-64のOSは動かねーよという。

VirtualBoxのvmを再度インストール

『CentOS 6.5 i386 Minimal (VirtualBox Guest Additions 4.2.16, Chef 11.6.0, Puppet 3.2.3)』を選択。

> cd C:\path\to\vagranfiledir\centos65
> vagrant box add centos_65 https://dl.dropbox.com/s/3fgr7lbvcpn51py/centos_6-5_i386.box
> vagrant init centos_65
> * Vagrantfile編集
> vagrant up

This kernel requires an x86-64 CPU, but only detected an i686 CPU.
Unable to boot - please use a kernel appropriate for your CPU.

あれー???
どうもcentos_65というvmがvagrant box addでは上書きされず、古いのが起動したっぽい。
仕方ないので名前を変える。

> vagrant destroy
> cd C:\programfiles\vagranfiledir\centos65_32
> vagrant box add centos_65_32 https://dl.dropbox.com/s/3fgr7lbvcpn51py/centos_6-5_i386.box
> vagrant init centos_65_32
> * Vagrantfile編集
> vagrant up

何度もダウンロードしてて無駄っぽいが、対処方法わからず。

Vagrantを起動

> vagrant up

> CentOS release 6.5 (Final)
> Kernel 2.6.32-431.11.2.e16.i686 on an i686
> localhost login:

ついにVirtualBoxが起動した。
やれやれ。
で、このID/パスワードは何だ?

http://weblabo.oscasierra.net/windows-vagrant-1/
ID/パスワード共にvagrantで入れた。

Vagrantにログイン

> vagrant ssh

> `ssh` executable not found in any directories in the %PATH% variable.

SSHなんてねえよ、Cygwinか何かSSH使えるものを入れろ、って言われた。
さすがにcmdでは駄目だったか。
PR


2014/06/27 23:14 | Comments(0) | PHP
PHPでLINQ
PHPでLINQを再現するライブラリ、GINQを試してみます。

そもそもLINQって何よ。
http://ja.wikipedia.org/wiki/%E7%B5%B1%E5%90%88%E8%A8%80%E8%AA%9E%E3%82%AF%E3%82%A8%E3%83%AA
> データ集合に対して標準化された方法でデータを問い合わせることを可能にする
http://msdn.microsoft.com/ja-jp/library/bb308959.aspx
> .NET Framework に汎用クエリ機能が追加され、リレーショナル データや XML データだけでなく、あらゆる情報ソースにクエリ機能が適用されます。

何のこっちゃ。
平たく言うと、配列やらオブジェクトやらその他諸々の集合に対してSQL(っぽいもの)でSELECTできちまうぜ、という代物らしい。
さっそく試してみましょう。
<?php
	
	// Ginq
	require_once('path/to/ginq/Ginq.php');
	
	$sample1 = ['John', 'Peter', 'Joe', 'Patrick', 'Donald', 'Eric', 'Joe'];
	
	// SELECT name FROM sample1 WHERE LENGTH(name) < 5
	$under5 = Ginq::from($sample1)
		->where(function($name){return strlen($name) < 5;})
		->toArray();
	
	// SELECT DISTINCT name FROM sample1 ORDER BY id DESC
	$reverse = Ginq::from($sample1)->reverse()->distinct()->toArray();
	
	// Employee PHPLinqのサンプルのコピペ
	class Employee {
		public $Id, $DepartmentId, $ManagerId, $Name, $Email, $Age;
		public function __construct($id = 0, $departmentId = 0, $managerId = 0, $name = '', $email = '', $age = '') {
			$this->Id				= $id;
			$this->DepartmentId 	= $departmentId;
			$this->ManagerId 		= $managerId;
			$this->Name 			= $name;
			$this->Email 			= $email;
			$this->Age				= $age;
		}
	}
	$employees = [
		new Employee(1, 1, 5, 'Maarten', 'maarten@example.com', 24),
		new Employee(2, 1, 5, 'Paul', 'paul@example.com', 30),
		new Employee(3, 2, 5, 'Bill', 'bill.a@example.com', 29),
		new Employee(4, 3, 5, 'Bill', 'bill.g@example.com', 28),
		new Employee(5, 2, 0, 'Xavier', 'xavier@example.com', 40)
	];
	
	// SELECT Email, SUBSTR(Email, INSTR(Email,'@')+1) FROM employees WHERE LENGTH(Name) = 4 ORDER BY Age DESC LIMIT 2 OFFSET 1
	$result = Ginq::from($employees)
		->where(function($employee){ return strlen($employee->Name) === 4; })
		->orderByDesc(function($employee){ return $employee->Age; })
		->drop(1)->take(2)
		->select(function($employee){ return [
			'EmailAddress' => $employee->Email,
			'Domain' => substr($employee->Email, strpos($employee->Email, "@") + 1)
			]; })
		->toArray();
コメントで書かれているSQLとほぼ同じ処理を、メソッドチェーンでSQLっぽく書くことができるようになりました。
SQLそのものではありませんが、同じようなかんじで使えます。
いちいちarray_filter()してforeachで抽出して、といった面倒な処理を書かなくて済みます。

前回のPHPLinqとほぼ同じですが、fromがIteratorに対応しているため、ArrayObjectやSPLなんかも突っ込めます。
また大抵のメソッドがcallableを受け取れるので、文字列で書かなければいけないPHPLinqより楽で汎用性があります。

まあ、このスライド見ればGinqの機能はだいたいわかります。
今回は使ってませんが、集計とかJOINとか他にもたくさん機能があり、また遅延実行にも対応しててパフォーマンスもよさげ。
けっこう使い勝手の良さそうなライブラリです。


2014/06/23 23:12 | Comments(0) | PHP
PHPでLINQ
PHPでLINQを再現するライブラリ、PHPLinqを試してみます。

そもそもLINQって何よ。
http://ja.wikipedia.org/wiki/%E7%B5%B1%E5%90%88%E8%A8%80%E8%AA%9E%E3%82%AF%E3%82%A8%E3%83%AA
> データ集合に対して標準化された方法でデータを問い合わせることを可能にする
http://msdn.microsoft.com/ja-jp/library/bb308959.aspx
> .NET Framework に汎用クエリ機能が追加され、リレーショナル データや XML データだけでなく、あらゆる情報ソースにクエリ機能が適用されます。

何のこっちゃ。
平たく言うと、配列やらオブジェクトやらその他諸々の集合に対してSQL(っぽいもの)でSELECTできちまうぜ、という代物らしい。
さっそく試してみましょう。
<?php
	
	// Linq include_path設定が必要
	set_include_path(get_include_path() . PATH_SEPARATOR . 'path/to/phplinq/Classes');
	require_once 'path/to/phplinq/Classes/PHPLinq/LinqToObjects.php';
	
	$sample1 = ['John', 'Peter', 'Joe', 'Patrick', 'Donald', 'Eric', 'Joe'];
	
	// SELECT name FROM sample1 WHERE LENGTH(name) < 5
	$result = from('$name')->in($sample1)
		->where('strlen($name) < 5')
		->select('$name');
	
	// SELECT DISTINCT name FROM sample1 ORDER BY id DESC
	$result = from('$name')->in($sample1)->reverse()->distinct('$name')->select();
	
	// Employee サンプルのコピペ
	class Employee {
		public $Id, $DepartmentId, $ManagerId, $Name, $Email, $Age;
		public function __construct($id = 0, $departmentId = 0, $managerId = 0, $name = '', $email = '', $age = '') {
			$this->Id				= $id;
			$this->DepartmentId 	= $departmentId;
			$this->ManagerId 		= $managerId;
			$this->Name 			= $name;
			$this->Email 			= $email;
			$this->Age				= $age;
		}
	}
	$employees = [
		new Employee(1, 1, 5, 'Maarten', 'maarten@example.com', 24),
		new Employee(2, 1, 5, 'Paul', 'paul@example.com', 30),
		new Employee(3, 2, 5, 'Bill', 'bill.a@example.com', 29),
		new Employee(4, 3, 5, 'Bill', 'bill.g@example.com', 28),
		new Employee(5, 2, 0, 'Xavier', 'xavier@example.com', 40)
	];
	
	// SELECT Email, SUBSTR(Email, INSTR(Email,'@')+1) FROM employees WHERE LENGTH(Name) = 4 ORDER BY Age DESC LIMIT 2 OFFSET 1
	$result = from('$employee')->in($employees)
		->where('$employee => strlen($employee->Name) === 4')
		->orderByDescending('$employee->Age')
		->skip(1)->take(2)
		->select('new {
			"EmailAddress" => $employee->Email,
			"Domain" => substr($employee->Email, strpos($employee->Email, "@") + 1)
		}');
コメントで書かれているSQLとほぼ同じ処理を、メソッドチェーンでSQLっぽく書くことができるようになりました。
SQLそのものではありませんが、同じようなかんじで使えます。
いちいちarray_filter()してforeachで抽出して、といった面倒な処理を書かなくて済みます。

一見便利ですが、欠点としては、対応しているのが配列とZend_Dbだけという点です。
ソースにはAdapter/MysqliやAdapter/Pdo/Mysqlなんてものがあるので、PDOを突っ込めるのかと思いきや、何故かZend_Db_Adapterを要求されます。
またArrayObjectなんてほぼ配列なのですが、これも対応していません。
サンプルではRSSのXMLを処理している例も載っているのですが、単にSimpleXMLElement::xpath()で配列にしてから突っ込んでるだけでした。
対応がもっと広がればどんどん便利になるかと思われましたが、2009年あたりで開発が停止しています。
残念。


2014/06/20 22:55 | Comments(0) | PHP
「Excel列名変換問題」をPHPで解く
http://blog.jnito.com/entry/20111102/1320253815
http://blog.jnito.com/entry/20111231/1325304733

数値とExcelのA1参照形式を相互変換する問題。

上限無しとかいう制限が入っていますが、とりあえず普通にintの範囲で記述します。
あちらの解答例もint越えを全く考慮してないので特に問題ないでしょう。

<?php
	class EXCEL{
	
		/**
		* Excelの列変換、数値から文字列に
		* @param int 「1」みたいな数値
		* @return String 「A」みたいな文字列
		*/
		public function getStr($input){
			$ret = '';
			do{
				$tmp = gmp_div_qr($input, 26);
				$input = gmp_intval($tmp[0]);
				$amari = gmp_intval($tmp[1]);
				// 割り切れる場合は0ではなく26として扱う
				if($amari === 0){
					$input--;
					$amari = 26;
				}
				$ret .= chr($amari+64);
			}while($input);
			
			return strrev($ret);
		}
		
		/**
		* Excelの列変換、文字列から数値に
		* @param String 「A」みたいな文字列
		* @return int 「1」みたいな数値
		*/
		public function getInt($input){
			$digit = strlen($input)-1;
			$ret = 0;
			for($i=0;$i<=$digit;$i++){
				$ret += (ord($input[$digit-$i])-64) * (26**$i);
			}
			return $ret;
		}
		
	}
	
	// 以下はテスト
	$test = [
		[1, 'A'],
		[2, 'B'],
		[3, 'C'],
		[25, 'Y'],
		[26, 'Z'],
		[27, 'AA'],
		[28, 'AB'],
		[51, 'AY'],
		[52, 'AZ'],
		[53, 'BA'],
		[54, 'BB'],
		[256, 'IV'],
		[16384, 'XFD'],
	];

	$excel = new EXCEL();
	foreach($test as $key=>$data){
		$answer = $excel->getStr($data[0]);
		if($answer !== $data[1]){
			print('えらー');
		}
	}
	foreach($test as $key=>$data){
		$answer = $excel->getInt($data[1]);
		if($answer !== $data[0]){
			print('えらー');
		}
	}

第1問第2問合わせても30分かかりませんでした。
つうか第2問のほうが簡単で5分くらいで終わったんだが。
まあPerlで解けと言われたら解けませんけどね。

考え方は簡単で、単に26で割っていくだけです。
26で割り切れる場合だけ注意が必要ですが、それ以外は単純にchr()で数値文字を変換すればいいです。
逆に数値にする場合は、26の例外すらも一切不要です。
何の問題もなく完成。

しかしまあ$amariはないな。


2014/06/13 23:32 | Comments(0) | PHP
DoctrineのDISTINCTは正しく動作しない
Doctrine_Query::create()->from('HOGE a')->distinct(true)->select('a.fuga');

// Expected result : SELECT DISTINCT fuga FROM hoge.
// Actual result : SELECT DISTINCT id, fuga FROM hoge.
見ての通り、発行されるSQLに勝手にID(主キー)が入るので正しく動作しない。
DISTINCTをselect()内に持ってきたとしても勝手に書き換えられるので駄目。

http://stackoverflow.com/questions/7188219/how-to-select-distinct-query-using-symfony2-doctrine-query-builder
http://www.developpez.net/forums/d793147/php/php-sgbd/orm/doctrine/doctrine-select-distinct/

回避方法は「GROUP BYを使え」らしい。
ふざけてるの?
Doctrine_Query::create()->from('HOGE a')->select('a.fuga')->groupBy(a.fuga);

// SELECT id, fuga FROM hoge group by fuga.
一応想定と同じ結果は取得できるのだが、意味が全く異なるので正しい動作とは言いかねる。

結論
「DoctrineでDISTINCTするときは直接SQL書こう」
Doctrine::getConnectionByTableName('HOGE')->execute('SELECT DISTINCT fuga FROM hoge')->fetchAll();

ところでDISTINCTよりEXISTSの方が早いという記事をよく見かけるんだけど、
結合しないfugaテーブルでEXISTSを使うにはどうすればいいんだろう?


2014/06/09 22:57 | Comments(0) | PHP
PHP5.6.0 デストラクタだけカレントディレクトリが違う
<?php
	class HOGE{
		public function __construct(){
			var_dump(getcwd()); // C:\path\to\DocumentRoot\test
		}
		public function fuga(){
			var_dump(getcwd()); // C:\path\to\DocumentRoot\test
		}
		public function __destruct(){
			var_dump(getcwd()); // C:\xampp
		}
	}
	
	$hoge = new HOGE();
	$hoge->fuga();

PHPのシャットダウン中にデストラクタが呼ばれた場合、カレントディレクトリがサーバのルートに変わってしまいます。
chdir()等でカレントディレクトリを変更していた場合にも元に戻ってしまいます。

まあデストラクタの問題というよりはシャットダウン処理の仕様で、register_shutdown_function()でも同じ現象が発生します。

<?php
	register_shutdown_function(function(){
		var_dump(getcwd()); // C:\xampp
	});

ここまでであれば、相当わかりにくいけどどうにか納得できないこともない。
そういう仕様ならそれに合わせるしかあるまい。
<?php
	class HOGE{
		public function __construct(){
			var_dump(getcwd()); // C:\path\to\DocumentRoot\test
		}
		public function fuga(){
			var_dump(getcwd()); // C:\path\to\DocumentRoot\test
		}
		public function __destruct(){
			var_dump(getcwd()); // C:\path\to\DocumentRoot\test
		}
	}
	
	$hoge = new HOGE();
	$hoge->fuga();
	$hoge = 1;

なんてこった。
これはあかんで。
こちらはスクリプト中でデストラクタが呼ばれるため、カレントディレクトリが継続しています。

回避手段としては、デストラクタでディレクトリ/ファイルを扱わないというのもありますが、仕様上難しいこともあるでしょう。
実用的には、
・定数か何かでパス定義しといて常にフルパスで扱う
で問題ないでしょう。

なおディレクトリ名がC:\になっているのはXAMPPを使っているからで、Linux環境でも再現します。
getcwd()が/とかになる。


2014/06/06 20:10 | Comments(0) | PHP
PHP5.6.0 「第14回オフラインリアルタイムどう書くの参考問題」をPHPで解く
http://qiita.com/Nabetani/items/66806c9dc14a96f2fd42
http://nabetani.sakura.ne.jp/hena/ord14crosscircle/
円周上のCrossing

正直さっぱりわからなかったので諦めて他人の解答を見たのだが、余計わからなかった。
とりあえず1文字追加するごとに増える本数を数え上げるという形式で実装してみたところ、後半の問題が時間がかかりすぎて終わらないという有様に。
どうすればいいんだろう。
<?php
	class CROSSCIRCLE{
		/**
		* 円周上のCrossing
		* @param String 「aabbca1bcb」みたいな文字列
		* @return int 「14」みたいな数値
		*/
		public function get($input){
			// 初期値
			$count = 0;
			
			// 頭からくるくる
			for($i=0; $i<strlen($input)-1; $i++){
				$pos = $i+2;
				// 先に同じ文字が見つかれば
				while(($pos = strpos($input, $input[$i], $pos)) !== false){
					// 該当の文字で左右に区切る
					$c1 = count_chars(substr($input, $i+1, $pos-$i-1), 1);
					$c2 = count_chars(substr_replace($input, '', $i, $pos-$i+1), 1);
					// 左右に同じ文字があれば、文字数の積が交点の数
					foreach($c1 as $key=>$val){
						if(isset($c2[$key])){
							$count += $val*$c2[$key];
						}
					}
					$pos++;
				}
			}
			// 半分
			return $count/2;
		}
	}
	
	// 以下はテスト
	$test = [
		['aabbca1bcb', '14'],
		['111ZZZ', '0'],
		['v', '0'],
		['ww', '0'],
		['xxx', '0'],
		['yyyy', '1'],
		['zzzzz', '5'],
		['abcdef', '0'],
		['abcaef', '0'],
		['abbaee', '0'],
		['abcacb', '2'],
		['abcabc', '3'],
		['abcdabcd', '6'],
		['abcadeabcade', '23'],
		['abcdeedcba', '0'],
		['abcdeaedcba', '8'],
		['abcdeaedcbad', '16'],
		['QQQQXXXX', '2'],
		['QwQQmQXmXXwX', '14'],
		['111222333', '0'],
		['aaAAaA', '4'],
		['121232313', '12'],
		['1ab1b', '1'],
		['abcdefbadcfe', '12'],
		['abxcdefbadcfex', '14'],
		['dtnwtkt', '0'],
		['mvubvpp', '0'],
		['moggscd', '0'],
		['kzkjzpkw', '2'],
		['fbifybre', '1'],
		['rrrfjryki', '1'],
		['wrbbdwsdwtx', '2'],
		['vvucugvxbvgx', '9'],
		['ojkjzyasjwbfjj', '5'],
		['ggffyuxnkyypifff', '5'],
		['vcgtcqlwrepwvkkogl', '4'],
		['xeqtmmgppwcjpcisogxbs', '4'],
		['lukltpeucrqfvcupnpxwmoj', '6'],
		['zpzswlkkoqwwndwpfdpkhtzgtn', '31'],
		['bkfeflagfvluelududqjcvfyvytfw', '45'],
		['rvqbhfmcjjqlpqzulzerxgyowiwrfkmhw', '26'],
		['qyxvpdtoeexbqsethwjwmqszcxxjnsdoeaet', '144'],
		['rjmsgmswhcolmpbhmpncziymydyalrcnevsrespj', '133'],
		['oxetnyjzjbysnwktfwzndlejfndsqeetsnjvsicyjehd', '395'],
		['wzvddnddzogywcqxbyvagbzmsmtcmrrlbnebmvhaemjouaqim', '219'],
		['karhphxcxqgsyorhusbumbqzocuzvnwzwcpxgsksrviihxrgsrhji', '461'],
		['oxgbononhqdxzmkysgijwvxljpaazmgkurkpffeuwywwuyxhyfkicgyzyc', '441'],
		['sdgsrddwsrwqthhdvhrjhgtxwgurgyiygtktgtughtogzaqmcafkljgpniddsvb', '1077'],
		['qemhecchkgzhxmdcsltwhpoyhkapckkkzosmklcvzkiiucrvzzznmhjfcdumuflavxik', '1711'],
		['ffqmsirwpxrzfkbvmmfeptkbhnrvfcywthkwkbycmayhhkgvuyecbwwofwthlmzruphrcujwhr', '2440'],
	];

	$crosscircle = new CROSSCIRCLE();
	foreach($test as $key=>$data){
		$answer = $crosscircle->get($data[0]);
		if($answer !== (int)$data[1]){
			print('えらー');
		}
	}
普通に数えた。

まず1文字目の'a'に注目。
次に出てくる'a'を探すと、2文字目の'a'は間に線を引けないのでパス。
次の'a'は6文字目なので、そこで文字列を分解して'abbc'と'1bcb'を得る。
分解した文字のうち、'a'と'1'は片方にしかないので線を引けない。
'c'は左右に1つずつなので交点数は1*1=1。
'b'は左右に2つずつなので交点数は2*2=4。
次の'a'は存在しない、これで1文字目の'a'については終了。

次に1文字目の'a'に注目。
とまあ繰り返していって全部の合計を出した。
実行時間は0.1秒以下になった。はやい。

なお、最後の$pos++をwhile条件に突っ込もうとすると何故かWarningが発生します。


2014/06/02 23:38 | Comments(0) | PHP
PHP5.6.0 「第15回オフラインリアルタイムどう書くの参考問題」をPHPで解く
http://qiita.com/Nabetani/items/0b56395d4c9e7c64b230
http://nabetani.sakura.ne.jp/hena/ord15subpalin/
回文の発掘
<?php
	class SUBPLAIN{
	
		/**
		* 回文の発掘
		* @param String 「I_believe_you_can_solve」みたいな文字列
		* @param int 現在の回文長
		* @return int 「9」みたいな数値
		*/
		public function get($input, $length=0){
			// 1文字以下であれば終了
			if(( $iLength = strlen($input)) < 2){ return $length+$iLength; }
			// 最初=最後だった場合問答無用でそれが正解
			if($input[0] === $input[strlen($input)-1]){
				return $this->get(substr($input, 1, -1), $length+2);
			}
			// それ以外の場合は全探査が必要
			$maxLength = [];
			// 全文字でくるくる
			for($i=0;$i<$iLength-1; $i++){
				// 該当文字と同じものを後ろからみつける
				$b = strrpos($input, $input[$i]);
				if( $b > $i){
					// 該当文字と同じものが後ろにある場合、その間の文字で再度回文発掘
					$maxLength[] = $this->get(substr($input, $i+1, $b-$i-1), $length+2);
				}elseif($b === $i){
					// 前後から見て同じなので終了
					$maxLength[] = $length+1;
				}
			}
			// 探索した中で最大長のものを返す
			return max($maxLength);
		}
		
	}
	
	// 以下はテスト
	$test = [
		['1234567890987654321', '19'],
		['a', '1'],
		['aa', '2'],
		['aaa', '3'],
		['ab', '1'],
		['aabb', '2'],
		['ABBA', '4'],
		['Abba', '2'],
		['1234567890', '1'],
		['1234567890987654321', '19'],
		['abcdcba', '7'],
		['0a1b2c3d4c5b6a7', '7'],
		['abcdcba0123210', '7'],
		['abcdcba_123210', '7'],
		['_bcdcba0123210', '7'],
		['abcddcba0123210', '8'],
		['abcdcba01233210', '8'],
		['a0bc1dc2ba3210a', '9'],
		['a0bc1ddc2ba3210', '8'],
		['3a0bc1ddc2ba3210', '10'],
		['11oooo1111o1oo1o111ooooooooooo', '20'],
		['11o1111o1111oo11ooo11111ooo1oo', '20'],
		['111111oo11o111ooo1o1ooo11ooo1o', '21'],
		['11o1o1o11oo11o11oo111o1o1o11oo', '27'],
		['oo111o1o11o1oo1ooo11o1o11o1o1o', '27'],
		['1o1oo11111o1o1oo1o1o1111oo1o1o', '28'],
		['QQooooQooooQQyQoyQQQyyyyQQoyoy', '15'],
		['oQoooQooooQyoyQoyoyyyQQyQQQQoQ', '16'],
		['yyyyyooyQyyyoyyQyyooyQoQoQQoQy', '17'],
		['yyQoyQoyyQyQQoyooooyyQQyQyooQy', '24'],
		['oQQooQoQyQQoyoQQoQyQyQyQoQoooo', '24'],
		['oQQyQQyyQyQQoooyQQyyyQQQyyQQoy', '25'],
		['WAk9iHI4jVDlStyudwTNqE138kwan2', '3'],
		['c43fIu1Mlz0K9hEVOgGcUdbeB5ksa7', '3'],
		['CAbYcW5VqHjzFdIkH_61PM0TsyRuie', '3'],
		['jInQnUvSayrJTsQWujovbbqW0STvoj', '10'],
		['iZDrvpUKgtj3FrZsZ4CLjrEgUdZzQG', '11'],
		['ROgYUOu6er_DA7nB6UGquO1GUHC62R', '11'],
		['Oh_be_a_fine_girl_kiss_me', '9'],
		['8086_6502_6809_Z80', '11'],
		['xcode_visualstudio_eclipse', '11'],
		['word_excel_powerpoint_outlook', '9'],
		['abcdefghijklmnopqrstuvwxyz0123', '1'],
	];

	$subpalin = new SUBPLAIN();
	foreach($test as $key=>$data){
		$answer = $subpalin->get($data[0]);
		if($answer !== (int)$data[1]){
			print('えらー');
		}
	}
どう作ろうか相当考えあぐねていたのですが、全探査でいいやと思い立ったら1時間で終わった。

最初=最後の分岐については、こちらの解説を見てから追加しました。
これが無い場合の実行時間は1.2秒、ある場合は実行時間0.12秒です。
PHPはやい。


2014/05/30 23:06 | Comments(0) | PHP
PHP5.6.0 「第16回オフラインリアルタイムどう書くの問題」をPHPで解く
http://qiita.com/Nabetani/items/6a9f5593d0f3d7e0568c
http://nabetani.sakura.ne.jp/hena/ord16boseg/

方眼紙を白と黒で塗り分けたとき、境界の線分の長さを求める。
極めて順当に実装。
<?php
	
	class BOSEG{
		/**
		* 境界線分
		* @param String 「060276724276」みたいな文字列
		* @return String 「6,2,1,1,0,1」みたいな文字列
		*/
		public function get($input){
			$boseg = array_fill(1, 6, 0);
			// 入力値をパース
			$input = array_map(function($input){
				return sprintf('%03d%03d', base_convert($input[0], 8, 2),base_convert($input[1], 8, 2));
			}, str_split($input, 2));
			
			// 横向きにチェック
			for($i=0;$i<5;$i++){
				$length=0;
				for($j=0;$j<=5;$j++){
					if($input[$i][$j] === $input[$i+1][$j]){
						// 上下が同じなら
						if($length > 0){
							// 違いがあったらそこで途切れる
							$boseg[$length]++;
							$length=0;
						}
					}elseif($j===5){
						// 最後だけ少し特殊
						if($input[$i][$j] !== $input[$i+1][$j]){
							$boseg[$length+1]++;
						}
					}else{
						// 違ってるのが続く
						$length++;
					}
				}
			}
			// 縦向きにチェック
			for($i=0;$i<5;$i++){
				$length=0;
				for($j=0;$j<=5;$j++){
					if($input[$j][$i] === $input[$j][$i+1]){
						if($length > 0){
							$boseg[$length]++;
							$length=0;
						}
					}elseif($j===5){
						if($input[$j][$i] !== $input[$j][$i+1]){
							$boseg[$length+1]++;
						}
					}else{
						$length++;
					}
				}
			}
			// 終了
			return implode(',',$boseg);
		}
		
	}
	
	// 以下はテスト
	$test = [
		['060276724276','6,2,1,1,0,1'],
		['770175454177','2,3,0,3,1,0'],
		['743733377170','9,3,1,0,0,0'],
		['724212121273','5,2,1,1,1,1'],
		['100000000000','3,0,0,0,0,0'],
		['000002000000','4,0,0,0,0,0'],
		['003622223600','0,4,0,4,0,0'],
		['520073737070','8,3,1,1,0,0'],
		['770077007700','0,0,0,0,0,5'],
		['555555555514','2,0,0,0,2,2'],
		['764252427600','4,0,4,0,2,0'],
		['774555554177','3,3,1,3,0,0'],
		['674574754557','11,5,0,1,0,0'],
		['000000000000','0,0,0,0,0,0'],
		['777777777777','0,0,0,0,0,0'],
		['774377777577','6,0,2,0,0,0'],
		['070777777777','0,1,1,0,0,0'],
		['373737373737','0,0,0,0,0,1'],
		['603260327725','30,0,0,0,0,0'],
		['466331144663','30,0,0,0,0,0'],
		['000000000242','3,2,0,0,0,0'],
		['567656043772','18,2,1,0,0,0'],
		['200763012420','15,4,1,0,0,0'],
		['400101140052','14,3,0,0,0,0'],
		['764767476476','13,2,0,1,0,0'],
		['001110140110','12,2,1,0,0,0'],
		['765405076527','16,3,0,1,0,0'],
		['377323370373','8,4,2,0,0,0'],
		['250541131216','11,5,2,0,0,0'],
		['744165741476','12,3,2,0,0,0'],
		['042101000300','10,3,0,0,0,0'],
		['002004554101','11,3,1,0,0,0'],
		['371707762706','15,1,1,0,0,0'],
		['130371310175','7,3,1,2,0,0'],
		['212537003613','13,2,1,1,1,0'],
		['157700063411','15,3,0,0,0,1'],
		['011500036007','6,7,1,0,0,0'],
		['743113313517','17,2,1,0,0,0'],
		['174105270405','13,3,1,1,0,0'],
		['427272200311','13,3,2,0,0,0'],
		['725370332237','12,5,1,1,0,0'],
		['005640420046','12,1,3,0,0,0'],
		['700350001101','14,3,1,0,0,0'],
		['577627744076','16,1,1,1,0,0'],
		['620332232007','10,4,2,1,0,0'],
		['260406401000','15,1,1,0,0,0'],
		['737272723276','5,0,0,0,3,0'],
		['000400040444','7,0,2,0,0,0'],
		['370222002177','13,2,2,0,0,0'],
		['372236024656','9,3,2,0,1,0'],
		['276131137003','11,6,2,0,0,0'],
		['742134007240','13,4,2,0,0,0'],
		['777721775571','13,1,2,0,0,0'],
		['700301232233','11,2,3,0,0,0'],
	];

	$boseg = new BOSEG();
	foreach($test as $key=>$data){
		$answer = $boseg->get($data[0]);
		if($answer !== $data[1]){
			print('えらー');
		}
	}
ループ最後のところだけ少し処理を追加してますが、それ以外は普通に順番に見ていってるだけです。
横向きと縦向きで思いっきり同じことをしているので、まとめればもっと記述を減らせますがもういいや。


2014/05/26 23:12 | Comments(0) | PHP
PHP5.6.0 「第20回オフラインリアルタイムどう書くの問題」をPHPで解く
http://qiita.com/Nabetani/items/5791f8ae1bb5d069a49b
http://nabetani.sakura.ne.jp/hena/ord20meetime/

会議に都合のいい時間を探します。
<?php
	class MEETING{
		/**
		* ミーティングの時間
		* @param String 「A1000-1130,B1230-1800,Z0800-2200」みたいな文字列
		* @return String 「1130-1230」みたいな文字列
		*/
		public function get($input){
			// 初期値
			$all1 = str_repeat('1', 480);
			$freetime = ['A'=>$all1, 'B'=>$all1, 'I'=>$all1, 'J'=>$all1, 'Z'=>$all1];
			$starttime = mktime(10, 0, 0);
			
			// 入力値を整形
			$input = explode(',', $input);
			
			// 都合の悪い時間帯は0にする
			foreach($input as $val){
				$start = (mktime(substr($val, 1, 2), substr($val, 3, 2), 0) - $starttime)/60;
				$end   = (mktime(substr($val, 6, 2), substr($val, 8, 2), 0) - $starttime)/60;
				if($start < 0){ $start = 0; }elseif($start>480){ $start=480; }
				if($end < 0){ $end = 0; }elseif($end>480){ $end=480; }
				for($i=$start; $i<$end; $i++){
					$freetime[$val[0]][$i] = 0;
				}
			}
			
			// GMPにする
			foreach($freetime as $key=>$val){
				$freetime[$key] = gmp_init($val, 2);
				// Zのみビット反転
				if($key==='Z'){
					$freetime[$key] = gmp_xor($freetime[$key], gmp_init($all1, 2));
				}
			}
			
			// 全員'1'が60回続く箇所を見つける
				// ABIZで一番早い時間
				$andi = sprintf('%0480s', gmp_strval(gmp_and(
					gmp_and($freetime['A'], $freetime['B']),
					gmp_and($freetime['I'], $freetime['Z']) ), 2));
				$starti = strpos($andi, str_repeat('1', 60));
				// ABJZで一番早い時間
				$andj = sprintf('%0480s', gmp_strval(gmp_and(
					gmp_and($freetime['A'], $freetime['B']),
					gmp_and($freetime['J'], $freetime['Z']) ), 2));
				$startj = strpos($andj, str_repeat('1', 60));
				// どっちも無ければ解なし
				if($starti === false && $startj === false){ return '-'; }
			
			// 整形して返却
				$start = $starti===false ? $startj : ( $startj===false ? $starti : min($starti, $startj));
				$return date('Hi', $starttime + $start*60) . '-' . date('Hi', $starttime + $start*60+3600);
		}
		
	}
	
	// 以下はテスト
	$test = [
		['A1050-1130,B1400-1415,I1000-1400,I1600-1800,J1100-1745,Z1400-1421,Z1425-1800', '1425-1525'],
		['A1000-1200,B1300-1800,Z1000-1215,Z1230-1800', '-'],
		/* 省略 */
	];

	$meeting = new MEETING();
	foreach($test as $key=>$data){
		$answer = $meeting->get($data[0]);
		if($answer !== $data[1]){
			print('えらー');
		}
	}

暇な時間は1、用事があれば0を入れたビット列を作り、1が60回続くところを探しています。
発想は簡単ですが、文字列に対して論理演算を使うと想定外の結果になるのでビット操作が面倒なことになっていたり、日付を文字列にするところが妙にややこしかったり、あと最後の三項演算子は絶対もっと簡単な書き方があるだろ。

なんともすっきりしない解答。
時間も無駄にかかっていて3時間くらい。
何故か日時をDateTimeにしようとしたり文字列のまま処理しようとしたりで大混乱。


2014/05/23 23:21 | Comments(0) | PHP

<<前のページ | HOME | 次のページ>>
忍者ブログ[PR]