●プログラム言語C#について、基本的な文法やライブラリについて取り上げる予定です。

 ・サンプルコードはWindows8.1 / .NET Framework4.5 で確認しています。
    ・2015年9月以降はWindows10 / .NET Framework4.5.2 / C#6 で確認しています。
    ・2017年3月以降はWindows10 / .NET Framework4.6.2 / C#7 で確認しています。
    ・2017年5月以降はWindows10 / .NET Framework4.7 / C#7 で確認しています。

久しぶりにC#言語を使ったら、以前とは様変わりしていました。
他の言語と混同しがちなものや2.0の頃と違っている点など確認しながら記事を書いています。

文法を網羅したいとか体系的に学びたい方はリンクページにあるMSDNや入門書等を参考にされた方がよいかと思います。

2017年09月21日

機会が平等なら結果も平等とはならないのかというシミュレーション

「100ドルを持った100人を1つの部屋に集めて、それぞれ無作為に選ばれた人に1ドルを渡したらどうなるでしょうか」という問題。近夏にちょっと話題になりました。

直観的には試行回数が増えれば結局は最初の状態に均衡するような気がしますが、実際にやってみると確実に偏りが出るそうです。本当でしょうか。ちょっとやってみます。コードは簡単です。

using System;
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int number = 100;   // 人数
            int count = 3000;   // 試行回数

            Random rand = new Random(Environment.TickCount);

            double[] members = new double[number];

            for (int n = 0; n < number; n++)
            {
                members[n] = 100;   // 初期値
            }

            for (int n = 0; n < count; n++)
            {
                for (int m = 0; m < number; m++)
                {
                    if (members[m] != 0)
                    {
                        int target = rand.Next(number);

                        members[m]--;
                        members[target]++;
                    }
                }
            }
            Array.Sort(members);
        }
    }
}

人数や試行回数や初期値を変えてなんどかやってみましたが、やはり分散していきました。
肝はこれだけですが、デバッガで結果を確認するとかイマイチなので、ちょっと改変してみます。
大量のデータを可視化といえばグラフなので、計算結果をグラフ表示してみます。
以下のような画面をつくります。(ちょっとコードが増えてますが、やってることは上と同じ)


Calc20170920.png

1.WPFのプロジェクトを作る。→ 作る。
2.画面にグラフコントロールを載せる → 標準部品にないので以下のアセンブリをナゲットする。
  System.Windows.Controls.DataVisualization.Toolkit
3.開始ボタンと現在の試行回数を表示するテキストボックスも載せる → 載せる

こんぐらいで、画面のXAMLは以下のようになります。

<Window x:Class="WpfDistribute.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ct="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
        Title="MainWindow" Height="350" Width="525">
    <Grid>

        <Button Content="Start" HorizontalAlignment="Left" Margin="30,10,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
        <TextBox  HorizontalAlignment="Left" Height="23" Margin="145,10,0,0" TextWrapping="Wrap" Text="{Binding Count}" VerticalAlignment="Top" Width="120"/>

        <ct:Chart x:Name="chart" >
            <ct:Chart.LegendStyle>
                <Style TargetType="Control">
                    <Setter Property="Width" Value="10" />
                    <Setter Property="Opacity" Value="0" />
                </Style>
            </ct:Chart.LegendStyle>
            <ct:Chart.Series>
                <ct:ColumnSeries ItemsSource="{Binding Members}" IndependentValuePath="ID" DependentValuePath="Value"  />
            </ct:Chart.Series>
        </ct:Chart>
        
    </Grid>
</Window>
Styleの定義がありますが、これはデフォで表示される凡例を非表示とするオマジナイです。

コードビハンド側はこんな感じ。DataContextにViewModelを設定してますが、コマンド使わずメソッドをそのまま呼んでたりします。MVVMの手抜き版って感じです。
using System.ComponentModel;

namespace namespace WpfDistribute
{
    // MainWindowのコードビハインド
    public partial class MainWindow : Window
    {
        private ViewModel _vm;

        public MainWindow()
        {
            InitializeComponent();

            _vm = new ViewModel();

            this.DataContext = _vm;
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            Task.Run(() => _vm.Calculate());
        }
    }
    // ViewModelの様な感じ
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public System.Collections.ObjectModel.ObservableCollection<Member> Members { get; set; }

        public int Count { get; set; } = 10000;

 
        public  ViewModel()
        {
            Members = new System.Collections.ObjectModel.ObservableCollection<Member>();

            for (int n = 0; n < 300; n++)
            {
                Members.Add(new Member { ID = n, Value = 1000 });
            }
        }
        // コマンドではなくてメソッド
        public void Calculate()
        {
            for (int n = 0; n < 30000; n++)
            {
                for (int m = 0; m < Members.Count; m++)
                {
                    if (Members[m].Pay())
                    {
                        Random rand = new Random(n + Environment.TickCount);
                        int target = rand.Next(Members.Count);
                        Members[target].Recieve();
                    }
                }

                Count = 30000 - n;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));

                var temp = Members.OrderByDescending(m => m.Value).ToList();

                Members = new System.Collections.ObjectModel.ObservableCollection<Member>(temp);
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Members)));
            }
        }
    }

   // モデル(かな?)
    public class Member : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public double ID { get; set; }

        private double _value;
        public double Value
        {
            get { return _value; }
            set
            {
                _value = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
            }
        }
        // 持ってたら払うけど、無いなら無理
        public bool Pay()
        {
            if(Value > 0)
            {
                --Value;

                return true;
            }

            return false;
        }

        public void Recieve() => ++Value;
    }
}
posted by RR at 00:33 | Comment(0) | サンプルプログラム  | このブログの読者になる | 更新情報をチェックする