質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

処理の軽量化について

受付中

回答 0

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 442

score 0

はじめに

こんにちは。いわゆるパラドゲーというものをご存知でしょうか。今回はその中でもVictoria 2の内部処理を想像して参考にしながら、自分のシステムを作り上げてみました。しかし、一番最初のテストの段階で既に処理が重すぎます。これの改善方法に知恵をお貸しいただけますと幸いです。

システムを簡潔に説明

Victoria 2をご存知でしたら想像に容易いのですが、知らない前提で説明いたします。
まず、プレイヤーが操作するものは国家です。国家には人口がおり、様々な職業や人種に分かれています。その人口が株式投資したり、会社を興したり、経済活動をすることで自国経済が動きます。プレイヤーが国家として市場に介入することはあれど、直接手を下すということはありません。そんなものを表現したいと思っています。

コード

using System;
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            int chanceOfPopExcution = 1;
            int chanceOfCompanyExcution = 1;
            var pop = new Pop(chanceOfPopExcution, chanceOfCompanyExcution);
            var company = new Company();
            Console.WriteLine(pop.popItems.Count);
            Console.WriteLine(company.companyItems.Count);
            pop.action();
            sw.Stop();
            Console.WriteLine("■処理Aにかかった時間");
            TimeSpan ts = sw.Elapsed;
            Console.WriteLine($" {ts}");
            Console.WriteLine($" {ts.Hours}時間 {ts.Minutes}{ts.Seconds}{ts.Milliseconds}ミリ秒");
            Console.WriteLine($" {sw.ElapsedMilliseconds}ミリ秒");
        }
    }

    class Goods
    {
        public double life = 0.2;
        public double everyday = 0.4;
        public double luxury = 1.1;
    }

    public class Company
    {
        public List<CompanyItem> companyItems;

        public Company()
        {
            using (var sr = new StreamReader(@"company.json", System.Text.Encoding.UTF8))
            {
                var jsonData = sr.ReadToEnd();
                companyItems = JsonConvert.DeserializeObject<List<CompanyItem>>(jsonData);
            }
        }
        public class CompanyItem
        {
            public int code { get; set; }
            public double capital { get; set; }
            public double asset { get; set; }
            public double revenue { get; set; }
            public double issued { get; set; }
            public double apprprt_price { get; set; }
            public double share_price { get; set; }
        }
    }

    public class Pop
    {
        public List<PopItem> popItems;
        public int chanceOfPopExcution;
        public int chanceOfCompanyExcution;
        public Pop(int _chanceOfPopExcution, int _chanceOfCompanyExcution)
        {
            chanceOfPopExcution = _chanceOfPopExcution;
            chanceOfCompanyExcution = _chanceOfCompanyExcution;
            using (var sr = new StreamReader(@"pop.json", System.Text.Encoding.UTF8))
            {
                var jsonData = sr.ReadToEnd();
                popItems = JsonConvert.DeserializeObject<List<PopItem>>(jsonData);
            }
        }

        public void action()
        {
            for (int i = 0; i < popItems.Count; i++)
            {
                var ob = new ChanceOfExcution();
                if (ob.calc(chanceOfPopExcution))
                {
                    Dictionary<string,double> consumption = consume(popItems[i]);
                    popItems[i].money = consumption["money"];
                    if (consumption["fulfilled"] == 3)
                    {
                        invest(popItems[i]);
                    }
                }
            }
            return;
        }

        public List<Dictionary<string, double>> invest(PopItem popItem)
        {
            var returnList = new List<Dictionary<string, double>>();
            var ob = new ChanceOfExcution();
            if (ob.calc(chanceOfCompanyExcution))
            {
                var companyItems = new Company().companyItems;

                double money = popItem.size * popItem.money;

                for (int i = 0; i < companyItems.Count; i++)
                {

                    var companyItem = companyItems[i];
                    if (popItem.size * popItem.money * 0.8 > money) break;
                    if (companyItem.share_price == 0 || companyItem.apprprt_price / companyItem.share_price >= 1)
                    {
                        var obj = new Random();
                        double rand = obj.Next(9, 12) / 10;
                        double investingPrice;
                        if (companyItem.share_price == 0)
                        {
                            investingPrice = 0.5 * Math.Round(rand, 1, MidpointRounding.AwayFromZero);
                        }
                        else
                        {
                            investingPrice = 0.5 * Math.Round(companyItem.share_price * rand, 1, MidpointRounding.AwayFromZero);
                        }
                        if (popItem.money / 10 > investingPrice)
                        {
                            double rand2 = obj.Next(5, 15) / 10;
                            double investingVolume = Math.Floor(money / 100 * rand2 / investingPrice);
                            var dic = new Dictionary<string, double>() { { "pop_code", popItem.code }, { "company_code", companyItem.code }, { "price", investingPrice }, { "volume", investingVolume } };
                            returnList.Add(dic);
                        }
                    }
                }
            }
            return returnList;
        }

        public Dictionary<string, double> consume(PopItem pop_item)
        {
            var goods = new Goods();
            double money = pop_item.money;
            double multiplyer = (100 / chanceOfPopExcution);
            if (money - goods.life * multiplyer < 0) {
                return new Dictionary<string, double>(){ {"fulfilled",0},{"money",money}};
            }
            else if(money - (goods.life + goods.everyday) * multiplyer < 0)
            {
                return new Dictionary<string, double>() { { "fulfilled", 1 }, { "money", money - goods.life } };
            }
            else if(money - (goods.life + goods.everyday + goods.luxury) * multiplyer < 0)
            {
                return new Dictionary<string, double>() { { "fulfilled", 2 }, { "money", money - (goods.life + goods.everyday) * multiplyer } };
            }
            else
            {
                return new Dictionary<string, double>() { { "fulfilled", 3 }, { "money", money - (goods.life + goods.everyday + goods.luxury) * multiplyer } };
            }
        }

        public class PopItem
        {
            public int code { get; set; }
            public int size { get; set; }
            public double money { get; set; }
            public List<Dictionary<string,double>> share { get; set; }
        }
    }

    public class ChanceOfExcution
    {
        public bool calc(int chance)
        {
            var ob = new Random();
            int rand = ob.Next(1, 10000);
            int num = chance * 100;
            return num >= rand;
        }
    }
}

さいごに

このテストはpythonで記述しましたが、最終的にはunityで作りたいのでC#でやり直します。ですが、本質的に動作が重すぎる点は言語のせいではないと思いますので、この時点で何かアドバイスをいただきたいと思い、質問させていただきました。

追記

pop.jsonは100,000件のデータを、company.jsonは500件のデータを持っています。これら合計して50,000,000件計算させるのは心が痛むので、確率でPopとCompanyが動くようにしました。どちらも1%の確率で処理させ、合計処理を5000件に抑えたのですが、それでも処理に1秒くらいかかってしまいます。
本番環境では、これよりもっと複雑な計算も行いますし、PopもCompanyも一桁多いくらいな想定です。今のままでは不可能ですが、前述のゲーム、Victoria 2ではこの状況に非常によく似た問題の対処が行われています。全く想像つきません。一緒に考えてもらえませんか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正の依頼

  • Zuishin

    2021/08/26 16:17 編集

    ざっと見たところ再帰はないし、ループは action と invest 中にそれぞれ一つだけで、invest は action の中のループから呼び出されているので、ロジックの中でボトルネックになるとすれば invest 中のループでしょう。
    しかし中で特に重い処理をしているわけでもないので、companies と pops の数が常識外れに多いのでなければ、C# に移植することで問題は解消する可能性もあります。

    キャンセル

  • Zuishin

    2021/08/26 20:33 編集

    8 から 9 桁回のループで 2 秒が目安です。
    結局 50000000 回ループしているのでそのくらいの時間はかかると思います。

    1% の確率で処理させるのであれば、本来のループ回数の 1% の数だけ乱数を発生させ、それを添え字に処理を行えば良いと思います。

    キャンセル

  • holy_cow

    2021/08/26 21:00

    Zuishinさん、なるほど、思いつきませんでした! 確かに文字通り99%無駄なループが発生していました。こういうミスを直しつつも、最終的にはデータを縮小する方針になりそうです。ありがとうございます!

    キャンセル

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る