私はいつも、C++がJava(そしてほとんどのゲームがC++で開発されている理由です)よりもずっと効率的であると聞いています。
JavaとC++の両方で、まったく同じアルゴリズムを使用して「8人の女王パズル」を解くための小さなアルゴリズムを書いた後、数または2乗を増やし始めました。 20または22 * 22でも、Javaの方がはるかに効果的です(C++の場合は66秒)に対して3秒です)。
理由はわかりませんが、C++を始めたばかりなので、パフォーマンスに大きな間違いを犯した可能性があります。そのため、何が起こっているのかを理解するのに役立つ情報を喜んで受け入れます。
以下は、Javaで使用するコードです。
import Java.awt.Point;
import Java.util.ArrayList;
import Java.util.List;
public class HuitDames {
/**
* La liste des coordnnées des dames.
*/
private static List<Point> positions = new ArrayList<>();
/**
* Largeur de la grille.
*/
private static final int LARGEUR_GRILLE = 22;
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
int i = 1;
placerDame(i);
for (Point point : positions) {
System.out.println("(" + point.x + "; " + point.y + ")");
}
}
/**
* Place une dame et return true si la position est bonne.
* @param i le numéro de la dame.
* @return si la position est bonne.
*/
private static boolean placerDame(int i) {
boolean bonnePosition = false;
for (int j = 1; j <= LARGEUR_GRILLE && bonnePosition == false; j++) {
Point emplacement = new Point(i, j);
positions.add(emplacement);
if (verifierPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
bonnePosition = true;
}
else {
positions.remove(i - 1);
}
}
return bonnePosition;
}
/**
* Vérifie que la nouvelle position n'est pas en prise avec une position déjà présente.
* @param position la position de la nouvelle dame.
* @return Si la position convient par rapport aux positions des autres dames.
*/
private static boolean verifierPrise(Point position) {
boolean nonPrise = true;
for (Point point : positions) {
if (!point.equals(position)) {
// Cas où sur la même colonne.
if (position.y == point.y) {
nonPrise = false;
}
// Cas où sur même diagonale.
if (Math.abs(position.y - point.y) == Math.abs(position.x - point.x)) {
nonPrise = false;
}
}
}
return nonPrise;
}
}
そして、以下はC++のコードです。
#include <iostream>
#include <list>
#include <math.h>
#include <stdlib.h>
using namespace std;
// Class to represent points.
class Point {
private:
double xval, yval;
public:
// Constructor uses default arguments to allow calling with zero, one,
// or two values.
Point(double x = 0.0, double y = 0.0) {
xval = x;
yval = y;
}
// Extractors.
double x() { return xval; }
double y() { return yval; }
};
#define LARGEUR_GRILLE 22
list<Point> positions;
bool verifierNonPrise(Point emplacement) {
bool nonPrise = true;
for (list<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
if (it->x() != emplacement.x()) {
if (it->y() == emplacement.y()) {
nonPrise = false;
}
if (abs(it->y() - emplacement.y()) == abs(it->x() - emplacement.x())) {
nonPrise = false;
}
}
}
return nonPrise;
}
bool placerDame(int i) {
bool bonnePosition = false;
for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
Point emplacement(i,j);
positions.Push_back(emplacement);
if (verifierNonPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
bonnePosition = true;
}
else {
positions.pop_back();
}
}
return bonnePosition;
}
int main()
{
int i = 1;
placerDame(i);
for (list<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
cout << "(" << it->x() << "; " << it->y() << ")" << endl;
}
return 0;
}
std::list
はC++のリンクリストですが、Java.util.ArrayList
は配列です。 std::list
沿って std::vector
。また、最適化を有効にしてコンパイルしてください。
std::find_if
に置き換えましたverifierNonPrise
アーリーリターンを変更します> javac HuitDames.Java
> time Java HuitDames
real 0m0.368s
user 0m0.436s
sys 0m0.042s
> g++ -O3 -std=c++11 HuitDames.cpp
> time ./a.out
real 0m0.541s
user 0m0.539s
sys 0m0.002s
#include <iostream>
#include <vector>
#include <cmath>
#include <stdlib.h>
#include <chrono>
#include <algorithm>
using namespace std;
typedef std::pair<int, int> Point;
#define LARGEUR_GRILLE 22
vector<Point> positions;
bool verifierNonPrise(Point const& emplacement) {
return std::find_if(positions.begin(), positions.end(), [&emplacement](Point const& val){
if (val.first != emplacement.first) {
if ((val.second == emplacement.second) || (abs(val.second - emplacement.second) == abs(val.first - emplacement.first))) {
return true;
}
}
return false;
}) == positions.end();
}
bool placerDame(int i) {
bool bonnePosition = false;
for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
Point emplacement(i,j);
positions.Push_back(emplacement);
if (verifierNonPrise(emplacement) && (i == LARGEUR_GRILLE || placerDame(i + 1))) {
bonnePosition = true;
}
else {
positions.pop_back();
}
}
return bonnePosition;
}
int main()
{
using std::chrono::system_clock;
system_clock::time_point begin_time = system_clock::now();
int i = 1;
placerDame(i);
for (vector<Point>::iterator it = positions.begin(); it!= positions.end(); it++) {
cout << "(" << it->first << "; " << it->second << ")" << endl;
}
system_clock::time_point end_time = system_clock::now();
long long elapsed_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - begin_time).count();
cout << "Duration (milliseconds): "
<< elapsed_milliseconds
<< std::endl;
}
C++ 11機能を使用して更新されたこのバージョンをテストします。 GCC 4.9.0で-std=c++11
を使用してテスト済み。 Celeron 1.6µGHz、512MB RAMでテスト済み。
私のPCでの時間:
オリジナル:期間(ミリ秒):12658
最初のバージョン:期間(ミリ秒):3616
最適化されたバージョン:期間(ミリ秒):1745
変更点は次のとおりです。
vector
の代わりにlist
を使用 ベンチマーク 、および Stroustrupからの単語 。ソース:
#include <iostream>
#include <vector>
#include <chrono>
#include <iomanip>
using namespace std;
typedef std::pair<int, int> Point;
#define LARGEUR_GRILLE 22
vector<Point> positions;
bool verifierNonPrise(const Point& emplacement) {
bool nonPrise = true;
for (const auto& p : positions) {
if (p.first != emplacement.first) {
if (p.second == emplacement.second) {
nonPrise = false;
}
if (abs(p.second - emplacement.second) ==
abs(p.first - emplacement.first)) {
nonPrise = false;
}
}
}
return nonPrise;
}
bool placerDame(int i) {
bool bonnePosition = false;
for (int j = 1; j <= LARGEUR_GRILLE && !bonnePosition; j++) {
Point emplacement(i, j);
positions.emplace_back(emplacement);
if (verifierNonPrise(emplacement) &&
(i == LARGEUR_GRILLE || placerDame(i + 1))) {
bonnePosition = true;
} else {
positions.pop_back();
}
}
return bonnePosition;
}
int main(int argc, char* argv[]) {
std::chrono::system_clock::time_point begin_time =
std::chrono::system_clock::now();
positions.reserve(LARGEUR_GRILLE);
placerDame(1);
for (const auto& p : positions) {
cout << "(" << p.first << "; " << p.second << ")" << endl;
}
std::chrono::system_clock::time_point end_time =
std::chrono::system_clock::now();
long long elapsed_milliseconds =
std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - begin_time).count();
std::cout << "Duration (milliseconds): " << elapsed_milliseconds
<< std::endl;
return 0;
}
いくつかのより深い変更。
変更点は次のとおりです。
ソース(更新されたいくつかの推奨事項):
#include <algorithm>
#include <iostream>
#include <vector>
#include <chrono>
#include <iomanip>
using namespace std;
struct Point {
int x, y;
};
#define LARGEUR_GRILLE 22
vector<Point> positions;
bool verifierNonPrise(const Point& emplacement) {
return find_if(positions.cbegin(), positions.cend(), [&emplacement](const Point& p) {
return (p.x != emplacement.x &&
(p.y == emplacement.y ||
abs(p.y - emplacement.y) == abs(p.x - emplacement.x)));
}) == positions.cend();
}
bool placerDame(int i) {
for (int j = 1; j <= LARGEUR_GRILLE; j++) {
Point emplacement{i, j};
positions.Push_back(emplacement);
if (verifierNonPrise(emplacement) &&
(i == LARGEUR_GRILLE || placerDame(i + 1))) {
return true;
} else {
positions.pop_back();
}
}
return false;
}
int main(int argc, char* argv[]) {
std::chrono::system_clock::time_point begin_time =
std::chrono::system_clock::now();
positions.reserve(LARGEUR_GRILLE);
placerDame(1);
for (const auto& p : positions) {
cout << "(" << p.x << "; " << p.y << ")" << endl;
}
std::chrono::system_clock::time_point end_time =
std::chrono::system_clock::now();
long long elapsed_milliseconds =
std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - begin_time).count();
std::cout << "Duration (milliseconds): " << elapsed_milliseconds
<< std::endl;
return 0;
}
私はここで死んだ馬を打ち負かしているかもしれませんが、JavaをC++に1行ずつ変換するだけで、const参照パラメーターなどを使用しなくても、C++はほとんどJavaの2倍の速度。すべての「構文最適化」の実装などは、影響はほとんどありません...
rep ~/Documents $ g++ -O3 Queen.cpp
rep ~/Documents $ javac Queen.Java
rep ~/Documents $ time Java Queen
(1; 1)
(2; 3)
(3; 5)
(4; 2)
(5; 4)
(6; 10)
(7; 14)
(8; 17)
(9; 20)
(10; 13)
(11; 19)
(12; 22)
(13; 18)
(14; 8)
(15; 21)
(16; 12)
(17; 9)
(18; 6)
(19; 16)
(20; 7)
(21; 11)
(22; 15)
real 0m4.806s
user 0m4.857s
sys 0m0.067s
rep ~/Documents $ time ./a.out
(1; 1)
(2; 3)
(3; 5)
(4; 2)
(5; 4)
(6; 10)
(7; 14)
(8; 17)
(9; 20)
(10; 13)
(11; 19)
(12; 22)
(13; 18)
(14; 8)
(15; 21)
(16; 12)
(17; 9)
(18; 6)
(19; 16)
(20; 7)
(21; 11)
(22; 15)
real 0m2.131s
user 0m2.113s
sys 0m0.000s
rep ~/Documents $
Queen.Java(英語に翻訳された)
import Java.awt.Point;
import Java.util.ArrayList;
import Java.util.List;
public class Queen {
private static List<Point> positions = new ArrayList<>();
private static final int GRID_SIZE = 22;
public static void main(String[] args)
{
int i = 1;
placeQueen(i);
for (Point point : positions)
{
System.out.println("(" + point.x + "; " + point.y + ")");
}
}
private static boolean placeQueen(int i)
{
boolean bIsGoodPos = false;
for (int j = 1; j <= GRID_SIZE && bIsGoodPos == false; j++)
{
Point emplacement = new Point(i, j);
positions.add(emplacement);
if (verifyPos(emplacement) && (i == GRID_SIZE || placeQueen(i + 1)))
{
bIsGoodPos = true;
}
else
{
positions.remove(i - 1);
}
}
return bIsGoodPos;
}
private static boolean verifyPos(Point position)
{
boolean bIsSafe = true;
for (Point point : positions)
{
if (!point.equals(position))
{
if (position.y == point.y)
{
bIsSafe = false;
}
if (Math.abs(position.y - point.y) == Math.abs(position.x - point.x))
{
bIsSafe = false;
}
}
}
return bIsSafe;
}
}
Queen.cpp
#include <cmath>
#include <vector>
#include <iostream>
using namespace std;
struct Point
{
int x, y;
Point(int ii, int jj):x(ii), y(jj){}
};
vector<Point> positions;
int GRID_SIZE = 22;
bool verifyPos(Point position)
{
bool bIsSafe = true;
for(int i = 0; i < positions.size(); ++i)
{
Point point = positions[i];
if(point.x != position.x || point.y != position.y)
{
if(position.y == point.y)
{
bIsSafe = false;
}
if(abs(position.y - point.y) == abs(position.x - point.x))
{
bIsSafe = false;
}
}
}
return bIsSafe;
}
bool placeQueen(int i)
{
bool bIsGoodPos = false;
for(int j = 1; j <= GRID_SIZE && bIsGoodPos == false; j++)
{
Point p(i, j);
positions.Push_back(p);
if(verifyPos(p) && (i == GRID_SIZE || placeQueen(i + 1)))
{
bIsGoodPos = true;
}
else
{
positions.pop_back();
}
}
return bIsGoodPos;
}
int main(void)
{
int i = 1;
placeQueen(i);
for(int i = 0; i < positions.size(); ++i)
{
Point p = positions[i];
cout << "(" << p.x << "; " << p.y << ")" << endl;
}
return 0;
}
ビットマップを使用する場合、C++は(古いコアi7-860で)21ミリ秒でそれを行うことができます。チェスボードのグラフィック表示には解決策を見つけるのに2倍の時間がかかるため、タイミングの実行についてはshowSoln()呼び出しをコメントアウトしました。
#include <iostream>
#include <iomanip>
#include <fstream>
#include <omp.h> //omp_get_wtime() is my favorite time function
using namespace std;
static const unsigned n(22); //size of board
static_assert(n<32,"use long unsigned for bit masks if n > 32");
static const unsigned mask((1U<<n)-1); //n wide bitmask with every bit set
void showSoln(unsigned* selCol, unsigned numSoln) { //show a solution
cout << "\nsolution " << numSoln << '\n';
for (unsigned row=0; row<n; ++row) {
for (unsigned col=0; col<n; ++col)
cout << (col==selCol[row]? " Q": " .");
cout << '\n';
}
}
void main() {
//for each row bitmasks that show what columns are attacked, 1 bit means attacked
unsigned ulAttack[n]; //cols attacked from upper left, shift right for next row
unsigned upAttack[n]; //cols attacked from straight up, same for next row
unsigned urAttack[n]; //cols attacked from upper right, shift left for next row
unsigned allAttack[n]; //OR of all attacks on given row
allAttack[0]= ulAttack[0]= upAttack[0]= urAttack[0]= 0; //no attacks on row 0
unsigned row= 0; //the row where now placing a queen
unsigned selCol[n]; //for each row the selected column
unsigned numSoln= 0; //count of soutions found
double wtime= omp_get_wtime();
for (;;) { //loop until find 1st (or all) solutions
if (allAttack[row]!=mask) { //if 'row' has a column not attacked
unsigned long bit;
_BitScanForward(&bit,~allAttack[row]); //find lowest column not attacked
//note - your compiler may have a different intrinsic for find lowest set bit
selCol[row]= bit; //remember selected column for this row
unsigned move= 1U<<bit; //convert selected column to bitmask
allAttack[row]|= move; //mark column attacked to prevent re-use
if (row==n-1) { //if move in last row have a soln
++numSoln;
showSoln(selCol,numSoln);
break; //remove this break if want all solutions
} else { //no solution yet, fill in rows below
unsigned nrow= row+1; //next row
//from attacks on this row plus 'move' decide attacks on row below
ulAttack[nrow]= (ulAttack[row] | move) >> 1;
upAttack[nrow]= (upAttack[row] | move);
urAttack[nrow]= ((urAttack[row] | move) << 1) & mask;
allAttack[nrow]= ulAttack[nrow] | upAttack[nrow] | urAttack[nrow];
row= nrow; //go to next row
}
} else { //else move on 'row' is impossible so backtrack
if (!row) //if backtrack from row 0 have found all solutions
break;
--row; //try next move in prior row
}
}
wtime= omp_get_wtime() - wtime;
cout << "numSoln= " << numSoln << '\n';
cout << "time= " << wtime*1000 << " msec\n";
}
また、座標にfloat/dooubleタイプを使用する理由はありません。
C++で浮動小数点absライブラリの呼び出しを強制しない場合、パフォーマンスを向上させる必要があります。
JavaはPoint座標を整数として保存します。 get関数はdoubleを返しますが、これはおそらくJavaで、次にc ++で最適化する方が簡単です。
あまり多くのメモリアクセスと集中的なコンピューティングを必要としないコードでは、Java an C++は小さい。
test.Java
import Java.lang.String;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.Random;
import Java.util.Scanner;
public class test{
private static Random gna=new Random();
public static double new_value(double value){
if (value<0.5) value=0;
return value*value;
}
public static void main(String[] args) {
long start_time = System.currentTimeMillis();
List<Double> ze_list=new ArrayList();
for (int i=0;i<1e8;i++){
double temp=new_value(gna.nextDouble());
ze_list.add(temp);
}
long end_time = System.currentTimeMillis();
System.out.println("Time (s) :"+ ((end_time-start_time)/1000));
Scanner input = new Scanner(System.in);
String inputval = input.next();
}
}
それをtest.cppと比較してください:
#include <iostream>
#include <vector>
#include <ctime>
#include <random>
using namespace std;
static default_random_engine dre1(time(0));
static uniform_real_distribution <double> urd1(0, 1);
static double new_value(double value){
if (value<0.5) value=0;
return value*value;
}
int main(void){
time_t tbegin,tend;
double texec=0;
tbegin=time(NULL);
vector<double> ze_list;
for (int i=0;i<1e8;i++){
double temp=new_value(urd1(dre1));
ze_list.Push_back(temp);
}
tend=time(NULL);
texec=difftime(tend,tbegin);
cout << "\nTime (s) " << texec << " s\n";
int val=0;
cin >> val;
return 0;
}
Macでテストしました。
多分Javaパフォーマンスを上げる可能性がありますが、どのように見えるかわかりません。