您的位置:首页 > 编程语言 > C语言/C++

一个C#和C++执行效率对比的简单实例

2012-09-22 22:29 886 查看
这里用一个算法题进行比较。

原题是见http://acm.hdu.edu.cn/showproblem.php?pid=4090,登载在/article/2391753.html

作者提供了一个比较快的答案。

我之前也尝试做了一个,没有用递归,但也没有用作者使用的格局保存的剪枝方案,比较慢,随后看了作者的方案后再整合进了一个基本等效的格局保存逻辑。

以下是作者的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
///  /article/2391753.html
/// </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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐