一個C#和C++執行效率對比的簡單實例

這裏用一個算法題進行比較。

原題是見http://acm.hdu.edu.cn/showproblem.php?pid=4090,登載在http://blog.csdn.net/woshi250hua/article/details/7997550

作者提供了一個比較快的答案。

我之前也嘗試做了一個,沒有用遞歸,但也沒有用作者使用的格局保存的剪枝方案,比較慢,隨後看了作者的方案後再整合進了一個基本等效的格局保存邏輯。

以下是作者的C++程序的基本等價的C#程序,

using System.Collections.Generic;

namespace HDU4090
{
    internal class SolveGemAndPrince2
    {
        private const int Max = 10;

        private struct Node
        {
            public int X;
            public int Y;
        }

        private readonly Node[] _qu = new Node[Max*Max];
        private readonly Dictionary<string, int> _hash = new Dictionary<string, int>();

        private readonly int[,] _dir = new[,]
            {
                {1, 0}, {1, 1}, {1, -1}, {-1, 0}, {-1, -1}, {-1, 1}, {0, 1}, {0, -1}
            };

        private static void Change(int[,] mmap, ref int n, ref int m)
        {
            int i, j, tn = 0, tm = 0;
            var k = new int[10];
            for (j = 0; j < m; ++j)
            {

                k[j] = 0;
                for (i = 0; i < n; ++i)
                    if (mmap[i, j] != 0) mmap[k[j]++, j] = mmap[i, j];
            }
            for (j = 0; j < m; ++j)
                if (k[j] != 0)
                {

                    for (i = 0; i < k[j]; ++i)
                        mmap[i, tm] = mmap[i, j];
                    for (i = k[j]; i < n; ++i)
                        mmap[i, tm] = 0;
                    tm++;
                    if (k[j] > tn) tn = k[j];
                }
            n = tn;
            m = tm;
        }

        private int Ok(int[,] temp, int[,] vis, int i, int j, int n, int m)
        {
            var cnt = 0;
            int head = 0, tail = 0;
            Node cur;

            cur.X = i;
            cur.Y = j;
            _qu[head++] = cur;
            vis[cur.X,cur.Y] = 1;

            while (tail < head)
            {
                cur = _qu[tail++];
                cnt++;
                int k;
                for (k = 0; k < 8; ++k)
                {
                    Node next;
                    next.X = cur.X + _dir[k,0];
                    next.Y = cur.Y + _dir[k,1];
                    if (next.X >= 0 && next.X < n
                        && next.Y >= 0 && next.Y < m
                        && vis[next.X,next.Y] == 0
                        && temp[next.X,next.Y] == temp[i,j])
                    {

                        _qu[head++] = next;
                        vis[next.X,next.Y] = 1;
                        temp[next.X,next.Y] = 0;
                    }
                }
            }
            temp[i,j] = 0;
            return cnt >= 3 ? cnt : 0;
        }

        static string GetHash(int[,] temp,int n,int m)
        {
            var s = "";
            for (var i = 0; i < n; ++i)
                for (var j = 0; j < m; ++j)
                    s += temp[i,j] + '0';
            return s;
        }

        static void Mem(int[,] temp, int[,] mmap, int n, int m)
        {
            for (var i = 0; i < Max; i++ )
                for (var j = 0; j < Max; j++)
                    temp[i, j] = 0;
            for (var i = 0; i < n; ++i)
                for (var j = 0; j < m; ++j)
                    temp[i,j] = mmap[i,j];
        }

        int Dfs(int[,] mmap,int n,int m)
        {

            if (n*m < 3) return 0;
            var temp = new int[Max,Max];
            int i, j;
            var vis = new int[Max,Max];
            var cnt = 0;

            for (i = 0; i < n; ++i)
                for (j = 0; j < m; ++j)
                    if (vis[i,j]==0 && mmap[i,j]!=0)
                    {
                        Mem(temp, mmap, n, m);
                        var ans = Ok(temp, vis, i, j, n, m);
                        if (ans >= 3)
                        {
                            ans = ans*ans;
                            int tn = n, tm = m;
                            Change(temp, ref tn, ref tm);
                            var s = GetHash(temp, tn, tm);
#if true
                            if (!_hash.ContainsKey(s))
                                ans += Dfs(temp, tn, tm);
                            else ans += _hash[s];
#else
                            ans += Dfs(temp, tn, tm);
#endif
                            if (ans > cnt) cnt = ans;
                        }
                    }

            _hash[GetHash(mmap, n, m)] = cnt;
            return cnt;
        }

        public int Solve(int n, int m, int k, int[,] gems)
        {
            _hash.Clear();
            return Dfs(gems, n, m);
        }
    }
}

再接下來是我的方案(純粹湊熱鬧,沒有參與這裏的對比),

using System;
using System.Collections.Generic;

namespace HDU4090
{
    public class SolveGemAndPrince
    {
        #region Nested types

        class Node
        {
            #region Nested types

            public class Gem
            {
                public int Row { get; set; }
                public int Col { get; set; }
            }

            public class Connective
            {
                public readonly List<Gem> Connected = new List<Gem>();
            }

            #endregion

            #region Properties

            private int[,] Gems { get; set; }
            public List<Connective> Connectives { get; private set; }
            public int Try { get; set; }
            public int Score { get; private set; }

            #endregion

            #region Constructors

            public Node(int rows, int cols, int[,] gems, int iniScore)
            {
                _rows = rows;
                _cols = cols;
                Score = iniScore;
                Connectives = new List<Connective>();
                Gems = gems;
                Segment();
            }

            #endregion

            #region Methods

            public string GetHash()
            {
                var hash = "";
                foreach (var gem in Gems)
                {
                    hash += gem.ToString() + '0';
                }
                return hash;
            }

            void AddToConnective(Connective connective, bool[,] visited, int r, int c)
            {
                visited[r, c] = true;
                connective.Connected.Add(new Gem { Row = r, Col = c });
                var imin = Math.Max(0, r - 1);
                var imax = Math.Min(_rows - 1, r + 1);
                var jmin = Math.Max(0, c - 1);
                var jmax = Math.Min(_cols - 1, c + 1);
                var cur = Gems[r, c];
                for (var i = imin; i <= imax; i++)
                {
                    for (var j = jmin; j <= jmax; j++)
                    {
                        if (visited[i, j]) continue;
                        var val = Gems[i, j];
                        if (val == cur)
                        {   // TODO recursive, improve it
                            AddToConnective(connective, visited, i, j);
                        }
                    }
                }
            }

            void Segment()
            {
                var visited = new bool[_rows, _cols];
                for (var i = 0; i < _rows; i++)
                {
                    for (var j = 0; j < _cols; j++)
                    {
                        if (visited[i, j] || Gems[i, j] == 0) continue;
                        var connective = new Connective();
                        AddToConnective(connective, visited, i, j);
                        if (connective.Connected.Count < 3) continue;
                        Connectives.Add(connective);
                    }
                }
            }

            public Node Action(int path)
            {
                var connective = Connectives[path];
                var gems = Copy();
                var firstNonZeroRow = _rows;
                var firstNonZeroCol = 0;

                foreach (var gem in connective.Connected)
                {
                    gems[gem.Row, gem.Col] = 0;
                }
                // processes falling
                var newcols = 0;
                for (var i = 0; i < _cols; i++)
                {
                    var k = _rows - 1;
                    for (var j = _rows - 1; j >= 0; j--)
                    {
                        if (gems[j, i] > 0)
                        {
                            gems[k--, i] = gems[j, i];
                        }
                    }
                    for (var t = 0; t <= k; t++)
                    {
                        gems[t, i] = 0;
                    }
                    if (k + 1 < firstNonZeroRow) firstNonZeroRow = k + 1;
                    if (k + 1 < _rows)
                    {   // non-empty
                        newcols++;
                    }
                    else if (i == firstNonZeroCol)
                    {
                        firstNonZeroCol = i;
                    }
                }
                // processes shifting

                var newrows = _rows - firstNonZeroRow;
                var newgems = new int[newrows, newcols];
                var tcol = 0;
                for (var j = firstNonZeroCol; j < _cols; j++)
                {
                    if (gems[_rows - 1, j] == 0) continue;  // empty column
                    for (var i = firstNonZeroRow; i < _rows; i++)
                    {
                        newgems[i - firstNonZeroRow, tcol] = gems[i, j];
                    }
                    tcol++;
                }
                var count = connective.Connected.Count;
                var scoreInc = count*count;
                return new Node(newrows, newcols, newgems, Score + scoreInc);
            }

            int[,] Copy()
            {
                var gems = new int[_rows,_cols];
                for (var i = 0; i < _rows; i++)
                {
                    for (var j =0; j < _cols; j++)
                    {
                        gems[i, j] = Gems[i, j];
                    }
                }
                return gems;
            }

            #endregion

            #region Fields

            private readonly int _rows;
            private readonly int _cols;

            #endregion
        }

        #endregion

        #region Methods

        /// <summary>
        ///  Solves the gem-and-prince problem presented in HDU-4090
        ///  http://acm.hdu.edu.cn/showproblem.php?pid=4090
        ///  found from the post at
        ///  http://blog.csdn.net/woshi250hua/article/details/7997550
        /// </summary>
        /// <param name="n">The number of rows the gem grid contains; might well be ignored in C#</param>
        /// <param name="m">The number of columns the gem grid contains; might well be ignored in C#</param>
        /// <param name="k">The inclusive upper bound of gem values of which the minimum is always 0 which means there is no gem</param>
        /// <param name="gems">The initial grid of gems</param>
        /// <returns>The highest score that can be achieved by the solution</returns>
        public int Solve(int n, int m, int k, int[,] gems)
        {
            _records.Clear();
            var root = new Node(n, m, gems, 0);
            var stack = new Stack<Node>();
            var highest = 0;
            stack.Push(root);

            while (stack.Count > 0)
            {
                var node = stack.Pop();


                if (node.Try >= node.Connectives.Count) continue;
                var newNode = node.Action(node.Try);
                node.Try++;
                stack.Push(node);

#if true    // optimisation
                var hash = newNode.GetHash();
                if (_records.ContainsKey(hash))
                {
                    var oldScore = _records[hash];
                    if (newNode.Score <= oldScore)
                        continue;
                }

                _records[hash] = newNode.Score;
#endif
                
                if (newNode.Score > highest)
                {
                    highest = newNode.Score;
                }
                stack.Push(newNode);
            }
            return highest;
        }

        #endregion

        #region Fields

        readonly Dictionary<string,int> _records = new Dictionary<string, int>(); 

        #endregion
    }
}


測試程序和樣本:

using System;

namespace HDU4090
{
    class Program
    {
        struct Sample
        {
            public int N, M, K;
            public int[,] Data;
        }

        private static Sample[] Samples = new Sample[]
            {
                new Sample
                    {
                        N = 5,
                        M = 5,
                        K = 5,
                        Data =
                            new[,]
                                {
                                    {1, 2, 3, 4, 5},
                                    {1, 2, 2, 2, 1},
                                    {1, 2, 1, 2, 1},
                                    {1, 2, 2, 2, 2},
                                    {1, 2, 3, 3, 5}
                                }
                    },
                new Sample
                    {
                        N = 3,
                        M = 3,
                        K = 3,
                        Data =
                            new[,]
                                {
                                    {1, 1, 1},
                                    {1, 1, 1},
                                    {2, 3, 3}
                                }
                    },
                new Sample
                    {
                        N = 4,
                        M = 4,
                        K = 3,
                        Data =
                            new[,]
                                {
                                    {1, 1, 1, 3},
                                    {2, 1, 2, 3},
                                    {1, 2, 1, 3},
                                    {3, 3, 3, 3}
                                }
                    },
                new Sample
                    {
                        N = 4,
                        M = 4,
                        K = 2,
                        Data =
                            new[,]
                                {
                                    {1, 2, 1, 2},
                                    {2, 1, 2, 1},
                                    {1, 2, 1, 2},
                                    {2, 1, 2, 1}
                                }
                    },
                new Sample
                    {
                        N = 8,
                        M = 8,
                        K = 6,
                        Data =
                            new[,]
                                {
                                    {1, 1, 1, 1, 1, 1, 1, 1},
                                    {2, 2, 2, 2, 2, 2, 2, 2},
                                    {3, 3, 3, 3, 3, 3, 3, 3},
                                    {4, 4, 4, 4, 4, 4, 4, 4},
                                    {5, 5, 5, 5, 5, 5, 5, 5},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6}
                                }
                    },
                new Sample
                    {
                        N = 8,
                        M = 8,
                        K = 6,
                        Data =
                            new[,]
                                {
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6},
                                    {6, 6, 6, 6, 6, 6, 6, 6}
                                }
                    },
            };


        private static int[] _key = {166,36,94,128,896,4096};

        static void Main(string[] args)
        {
            var solve = new SolveGemAndPrince();
            var solve2 = new SolveGemAndPrince2();
            var time1 = DateTime.Now;
            for (var i = 0; i < 10000; i++ )
            {
                int t = 0;
                foreach (var sample in Samples)
                {
                    var result = solve.Solve(sample.N, sample.M, sample.K, sample.Data);
                    //var result = solve2.Solve(sample.N, sample.M, sample.K, sample.Data);
                    //Console.WriteLine("Highest score achievable = {0}", result);
                    if (result != _key[t++])
                    {
                        Console.WriteLine("error!");
                        return;
                    }
                }
            }
            var time2 = DateTime.Now;
            var span = time2 - time1;
            Console.WriteLine("Done {0}", span.TotalSeconds);
        }
    }
}

將工程編譯都調整到速度最大優化,編譯環境爲Visual Studio 2012,C++編譯爲32位,C#爲Any CPU,運行於CORE i7。結果是運行10000次循環,算法相同的C++程序的速度大約是C#的6倍。當然這個倍率有點高於我的預料,可能程序中還有需要對針對C#進行改進和優化的地方(例如減少堆分配等),但同樣C++也有提升空間(當然原作者已經做的很好了)。這個程序包含棧/遞歸和字典數據結構和一些邏輯和計算操作。總的來說在最大速度優先編譯情況下,C++相對C#的執行效率優勢還是比較明顯。等過一陣子有空針對這個實例再做一次review和各自優化再評估一下結果。

關於效率這個問題,stackoverflow上有一些討論可以參考:

http://stackoverflow.com/questions/145110/c-performance-vs-java-c

http://stackoverflow.com/questions/3961426/c-sharp-vs-c-performance-comparison


附,對應的C++程序(由原作者所作,略作修改並用於執行效率對比)

// HDU4090cpp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <string>
#include <string.h>
#include <time.h>

using namespace std;
#define MAX 10


struct node {

    int x,y;
}qu[MAX*MAX];

map<string,int> _hash;
int gmmap[8][MAX][MAX];
int (*mmap)[MAX];
int ans;
int n,m,K,total;
int dir[8][2] = {{1,0},{1,1},{1,-1},{-1,0},{-1,-1},{-1,1},{0,1},{0,-1}};


void Print(int temp[][MAX],int n,int m) {

    for (int i = n-1; i >= 0; --i)
        for (int j = 0; j < m; ++j)
            printf("%d%c",temp[i][j],j==m-1?'\n':' ');
}
void change(int mmap[][MAX],int &n,int &m){

    int i,j,k[10],tn = 0,tm = 0;
    for (j = 0; j < m; ++j) {
        
        k[j] = 0;
        for (i = 0; i < n; ++i)
            if (mmap[i][j]) mmap[k[j]++][j] = mmap[i][j];
    }
    for (j = 0; j < m; ++j) 
        if (k[j]) {
            
            for (i = 0; i < k[j]; ++i)
                mmap[i][tm] = mmap[i][j];
            for (i = k[j]; i < n; ++i)
                mmap[i][tm] = 0;
            tm++;
            if (k[j] > tn) tn = k[j];
        }
    n = tn,m = tm;
}
int Ok(int temp[][MAX],int vis[][MAX],int i,int j,int n,int m) {

    int cnt = 0,k;
    int head = 0,tail = 0;
    node cur,next;


    cur.x = i,cur.y = j;
    qu[head++] = cur;
    vis[cur.x][cur.y] = 1;


    while (tail < head) {

        cur = qu[tail++];
        cnt++;
        for (k = 0; k < 8; ++k) {

            next.x = cur.x + dir[k][0];
            next.y = cur.y + dir[k][1];
            if (next.x >= 0 && next.x < n
                    && next.y >= 0 && next.y < m
                    && vis[next.x][next.y] == 0
                    && temp[next.x][next.y] == temp[i][j]) {

                qu[head++] = next;
                vis[next.x][next.y] = 1;
                temp[next.x][next.y] = 0;
            }
        }
    }
    temp[i][j] = 0;
    return cnt >= 3 ? cnt :  0;
}
string Get_hash(int temp[][MAX],int n,int m) {

    string s = "";
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j)
            s += temp[i][j] + '0';
    return s;
}
void mem(int temp[][MAX],int mmap[][MAX],int n,int m) {

    memset(temp,0,sizeof(temp));
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j)
            temp[i][j] = mmap[i][j];
}
int Dfs(int mmap[][MAX],int n,int m) {

    if (n * m < 3)  return 0;
    int temp[MAX][MAX],i,j;
    int vis[MAX][MAX],cnt = 0,ans;
    memset(vis,0,sizeof(vis));


    for (i = 0; i < n; ++i)
        for (j = 0; j < m; ++j)
            if (!vis[i][j] && mmap[i][j]) {

                mem(temp,mmap,n,m);
                ans = Ok(temp,vis,i,j,n,m);
                if (ans >= 3) {

                    ans = ans * ans;
                    int tn = n,tm = m;
                    change(temp,tn,tm);
                    string s = Get_hash(temp,tn,tm);
                    if (_hash.find(s) == _hash.end()) 
                         ans += Dfs(temp,tn,tm);
                    else ans += _hash[s];
                    if (ans > cnt) cnt = ans;
                }
            }


     _hash[Get_hash(mmap,n,m)] = cnt;
     return cnt;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int i,j,k;
	int key[] = {166,36,94,128,896,4096};
	int t=0;
	FILE *fp = fopen("D:\\temp\\samples.txt", "r");
	int testnum = 0;
	while (fscanf(fp, "%d%d%d",&n,&m,&K) != EOF) 
	{
		for (i = n-1; i >= 0; --i)
			for (j = 0; j < m; ++j)
				fscanf(fp, "%d",&gmmap[testnum][i][j]);
		testnum++;
	}
	
	time_t t1;
	time(&t1);

	for (int C = 0; C < 10000; C++)
	{
		t = 0;
		for (int t=0; t<testnum; t++)
		{
			_hash.clear();
			int res = Dfs(gmmap[t],n,m);
			if (res != key[t])
			{
				printf("error\n");
				return 0;
			}
		   t++;
		}
	}
	time_t t2;
	time(&t2);
	double diff=difftime(t2,t1);
	printf("done %f\n", diff);
	fclose(fp);
}
發佈了200 篇原創文章 · 獲贊 227 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章