طراحی سایت و برنامه نویسی

آموزش طراحی سایت و برنامه نویسی

طراحی سایت و برنامه نویسی

آموزش طراحی سایت و برنامه نویسی

رنگ آمیزی گراف به روش حریصانه — به زبان ساده

در این مطلب، ابتدا مساله رنگ‌آمیزی گراف و انواع آن و سپس، رنگ آمیزی گراف به روش حریصانه مورد بررسی قرار گرفته است. همچنین، الگوریتم مذکور در زبان‌های برنامه‌نویسی ++C و «جاوا» (Java) پیاده‌سازی شده است. مساله رنگ آمیزی گراف، به بحث تخصیص رنگ به راس‌های گراف بر اساس محدودیت‌های مشخصی می‌پردازد. انواع مسائل رنگ‌آمیزی گراف در ادامه بیان شده‌اند.

رنگ‌آمیزی راس‌ها: این مساله، یکی از معروف‌ترین مسائل رنگ‌آمیزی گراف است. در مساله رنگ‌آمیزی راس‌ها، هدف آن است که با تعداد m رنگ داده شده، راس‌های گراف به گونه‌ای رنگ شوند که هیچ دو راس مجاوری دارای رنگ مشابه نباشند. دیگر مسائل رنگ‌آمیزی گراف مانند رنگ‌آمیزی یال‌ها (هیچ راسی بین دو یال با رنگ مشابه وجود نداشته باشد) و رنگ‌آمیزی چهره (رنگ‌آمیزی نقشه جغرافیایی) را می‌توان به مساله رنگ‌آمیزی راس‌ها تبدیل کرد. پیش از این، در مطلب «حل مساله رنگ آمیزی گراف با الگوریتم پس گرد»، این مساله با استفاده از الگوریتم «پس‌گرد» (Backtracking) حل شد و در این مطلب، این مساله با استفاده از الگوریتم «حریصانه» (Greedy) حل خواهد شد.

شماره کروماتیک: کوچکترین عدد از (تعداد) رنگ‌ها که برای رنگ‌آمیزی گراف G مورد نیاز است، شماره کروماتیک نامیده می‌شود. برای مثال، گراف زیر را می‌توان حداقل با 3 رنگ، رنگ‌آمیزی کرد.

رنگ آمیزی گراف به روش حریصانه

مساله پیدا کردن عدد کروماتیک برای یک گراف داده شده، «ان‌پی کامل» (NP-Complete) است.

کاربردهای مساله رنگ آمیزی گراف

مساله رنگ‌آمیزی گراف، کاربردهای بسیار زیادی دارد که به برخی از آن ها در زیر اشاره شده است.

برنامه‌ریزی روی جدول زمانی: فرض می‌شود که قرار است برنامه امتحانات یک دانشگاه تنظیم شود. لیستی از موضوعات مختلف وجود دارد و دانشجویان گوناگون دروس مختلف را ثبت کرده‌اند. بسیاری از دانشجویان دروس مشترکی را اخذ کرده‌اند (در واقع برخی از دروس، دانشجویان مشترکی دارند). چطور می‌توان امتحانات را به گونه‌ای برنامه‌ریزی کرد که هیچ دو امتحانی که یک دانش‌آموز مشترک دارند، در یک زمان واحد نباشند؟ حداقل بازه‌های زمانی مورد نیاز برای برگزاری کلیه امتحانات چه میزان است؟ این مساله را می‌توان به عنوان مساله گراف نشان داد که در آن، هر راس یک موضوع و هر یال بین دو راس به معنی وجود دانش‌آموز مشابه است. پس برنامه‌ریزی جدول زمانی امتحانات یک مساله رنگ‌آمیزی گراف است که در آن حداقل بازه‌های زمانی برابر با عدد کروماتیک گراف است.

تخصیص فرکانس رادیویی موبایل: هنگام تخصیص فرکانس‌های رادیویی به برج‌ها، این شرط وجود دارد که فرکانس‌های تخصیص پیدا کرده به کلیه برج‌ها در موقعیت یکسان، باید متفاوت باشند. چگونه می‌توان فرکانس‌ها را با این محدودیت تخصیص داد؟ حداقل تعداد فرکانس‌های مورد نیاز چند تا است؟ این مساله نیز نمونه‌ای از مسائل رنگ‌آمیزی گراف است که در آن، هر برج نشانگر یک راس و یال بین دو برج نشانگر آن است که آن‌ها در رِنج یکدیگر قرار دارند.

سودوکو: سودوکو نیز نوعی از مسائل رنگ‌آمیزی گراف محسوب می‌شود که در آن، هر سلول نشان‌گر یک راس است و اگر راس‌ها در سطر، ستون یا بلوک مشابهی باشند، یک یال بین آن‌ها وجود دارد.

تخصیص ثبات: در بهینه‌سازی کامپایلر، «تخصیص ثبات» (Register Allocation) فرآیند تخصیص تعداد بالایی از متغیرهای برنامه هدف در تعداد کمی از ثبات‌های «واحد پردازش مرکزی» است. این مساله نیز از جمله مسائل رنگ‌آمیزی گراف است.

گراف دو بخشی: می‌توان با رنگ‌آمیزی یک گراف تنها با استفاده از دو رنگ، بررسی کرد که آیا گراف دو بخشی است یا خیر. اگر بتوان گراف را تنها با دو رنگ، رنگ‌آمیزی کرد، گراف دو بخشی است.

رنگ‌آمیزی نقشه: رنگ‌آمیزی نقشه‌های جغرافیایی کشورها یا شهرهای یک کشور به صورتی که در آن هیچ دو کشور یا شهر هم‌جواری دارای رنگ مشابه نباشند نیز از جمله مسائل رنگ‌آمیزی گراف است. بر اساس «قضیه چهار رنگ» (Four Color Theorem)، برای رنگ‌آمیزی هر نقشه به صورتی که هیچ دو کشور/شهر مجاوری دارای رنگ مشابه نباشند، فقط چهار رنگ کافی است. در نقشه‌های ساده‌تر، تنها سه رنگ کافی است و از رنگ چهارم برای برخی نقشه‌ها استفاده می‌شود.

سایر کاربردها: مساله رنگ‌آمیزی گراف، کاربردهای متعدد و متنوع دیگری نیز دارد. برای مثال، شبکه‌ای از هزاران سرور وجود دارد که از آن‌ها برای توزیع محتوا در اینترنت استفاده می‌شود. آن‌ها هر هفته نرم‌افزارها یا به روز رسانی‌های جدیدی را نصب می‌کنند. به روز رسانی‌ها نمی‌تواند روی همه سرورها به طور هم‌زمان اتفاق بیفتد زیرا ممکن است فعالیت سرور طی مراحل نصب متوقف شود. همچنین، نباید هر بار تنها یکی از سرورها به روز رسانی شود زیرا این کار زمان زیادی می‌برد. بنابراین، سرورهای زیادی هستند که نباید هم‌زمان فعالیت آن‌ها متوقف شود زیرا فعالیت‌های حیاتی انجام می‌دهند و در عین حال به دلیل زمان‌بر بودن پروسه بیان شده برای تک به تک آن‌ها به طور جداگانه، این کار نیز ممکن نیست. مساله بیان شده، حالت متداولی از کاربرد مساله رنگ‌آمیزی گراف برای انجام زمان‌بندی است. شایان توجه است که برای گرافی با ۷۵۰۰۰ گره، تنها ۸ رنگ برای رنگ‌آمیزی کافی است. بنابراین، در مساله مربوط به سرورها نیز در این شرایط، می‌توان کار به روز رسانی را در هشت گذر انجام داد.

رنگ آمیزی گراف به روش حریصانه

در مساله رنگ‌آمیزی راس‌های گراف، چنانکه پیش از این نیز بیان شد، هدف رنگ کردن راس‌ها به شیوه‌ای است که هیچ دو راس مجاوری دارای رنگ مشابه نباشند. هیچ الگوریتم کارآمدی برای رنگ‌آمیزی گراف با حداقل رنگ‌های ممکن وجود ندارد و این مساله از جمله مسائل «ان‌پی کامل» (NP Complete) محسوب می‌شود. «الگوریتم‌های تقریبی» (Approximate Algorithms) گوناگونی برای حل این مساله وجود دارند. در ادامه، روش حریصانه برای مساله رنگ‌آمیزی گراف مورد بررسی قرار گرفته است. این روش تضمین نمی‌کند که کمترین تعداد رنگ‌ها را استفاده کند، اما حد بالا در تعداد رنگ‌ها را تضمین می‌کند. الگوریتم پایه هرگز نمی‌تواند بیش از d+1 رنگ که در آن d برابر با درجه بیشینه راس‌ها در گراف داده شده است را استفاده کند.

الگوریتم حریصانه پایه

  1. اولین راس را با اولین رنگ، رنگ‌آمیزی کن.
  2. اقدامات زیر را برای v-1 راس باقیمانده انجام بده
    1. راس کنونی را با رنگ دارای کمترین شماره‌گذاری (کمریتن تعداد استفاده) که پیش از این برای هیچ راس مجاوری استفاده نشده، رنگ‌آمیزی کن. اگر همه رنگ‌هایی که پیش از این استفاده شده‌اند در راس‌های مجاور این راس وجود دارند، یک رنگ جدید به راس v تخصیص بده.

کد رنگ آمیزی گراف به روش حریصانه در ++C

کد رنگ آمیزی گراف به روش حریصانه در جاوا

خروجی

Coloring of graph 1
Vertex 0 --->  Color 0
Vertex 1 --->  Color 1
Vertex 2 --->  Color 2
Vertex 3 --->  Color 0
Vertex 4 --->  Color 1

Coloring of graph 2
Vertex 0 --->  Color 0
Vertex 1 --->  Color 1
Vertex 2 --->  Color 2
Vertex 3 --->  Color 0
Vertex 4 --->  Color 3

پیچیدگی زمانی الگوریتم بالا در بدترین حالت برابر با (O(V2 + E است.

تحلیل الگوریتم پایه

الگوریتم بالا، از کمترین تعداد ممکن رنگ‌ها استفاده نمی‌کند. همچنین، تعداد رنگ‌های انتخاب شده گاهی بستگی به ترتیب راس‌های پردازش شده دارند. برای مثال، دو گراف زیر را می‌توان در نظر گرفت. در گراف پایین، راس‌های 3 و 4 جا به جا شده‌اند. اگر راس‌های ۰، 1، 2، 3 و 4 در گراف بالا در نظر گرفته شوند، می‌توان گراف را با استفاده از 3 رنگ، رنگ‌آمیزی کرد. اما در صورتی که راس‌های ۰، 1، 2، 3 و 4 در گراف پایین در نظر گرفته شوند، نیاز به چهار رنگ خواهد بود.

حل مساله رنگ آمیزی گراف با الگوریتم حریصانه

بنابراین، ترتیبی که بر اساس آن راس‌ها انتخاب می‌شوند، حائز اهمیت است. پژوهشگران گوناگون، راه‌های مختلفی را پیدا کرده‌اند که نسبت به الگوریتم میانگین، پاسخ بهتری دارد. از جمله شناخته شده‌ترین این روش‌ها، الگوریتم «وِلش-پاول» (Welsh–Powell Algorithm) است که راس‌ها را به ترتیب نزولی درجات در نظر می‌گیرد.

الگوریتم پایه چطور حد بالاتر از d+1 را تضمین می‌کند؟

در اینجا، d حداکثر درجه در گراف داده شده است. به دلیل آنکه d حداکثر درجه است، یک راس نمی‌تواند به بیش از d راس متصل شده باشد. هنگامی که یک راس رنگ‌آمیزی می‌شود، حداکثر d رنگ ممکن است توسط همسایگان آن راس مورد استفاده قرار گرفته باشند. برای رنگ‌آمیزی این راس، نیاز به انتخاب کمترین تعداد رنگ‌ها است که توسط راس‌های مجاور استفاده نشده‌اند. اگر راس‌ها با اعداد 1، 2 و … رنگ‌آمیزی شده باشند، مقدار کوچکترین عدد باید چیزی بین 1 تا d+1 باشد (توجه به این نکته لازم است که اعداد در حال حاضر توسط راس‌های مجاور گرفته شده‌اند). این مساله را می‌توان از طریق استنتاج نیز ثابت کرد.


منبع: فرادرس

ساخت اپلیکیشن لانچر اندروید — به زبان ساده

ممکن است تاکنون خواسته باشید اپلیکیشنی بنویسید که کاربرانتان بتوانند آن را در «حالت کیوسک» (Kiosk Mode) اجرا کنند تا همچنان امکان اجرای اپلیکیشن‌های دیگر را روی دستگاه‌های خود داشته باشند. هدف از این کار آن نیست که اپلیکیشن‌های ثانویه از طریق SDK یا دیگر ارتباط‌های درون پردازشی با هم یکپارچه شوند، بلکه قصد ما صرفاً این است که بتوانیم این اپلیکیشن‌های ثانویه را از داخل اپلیکیشن اولیه باز یا لانچ کنیم. در واقع این اپلیکیشن یک لانچر اندروید خواهد بود.

شاید بهتر باشد ابتدا مفهوم حالت کیوسک را توضیح دهیم. زمانی که یک اپلیکیشن در حالت کیوسک اجرا می‌شود، کاربر نمی‌تواند از اپلیکیشن خارج شود. در این حالت صفحه همواره روشن است. اپلیکیشن همواره در پیش‌زمینه است. نوار نوتیفیکیشن معمولاً پنهان یا غیرفعال شده و تقریباً همیشه دکمه Home غیرفعال می‌شود. این وضعیت عملاً موجب می‌شود که کاربر اپلیکیشن همواره درون همان اپلیکیشن منفرد باقی بماند. بنابراین با این توضیح احتمالاً اکنون می‌توانید متوجه شوید که چرا لازم است اپلیکیشن‌های ثانویه از داخل اپلیکیشن اصلی اجرا شوند.

لانچ کردن اپلیکیشن‌های دیگر از داخل یک اپلیکیشن روی اندروید، خود چالش بزرگی محسوب نمی‌شود. قطعه کدهای زیادی در بستر آنلاین وجود دارند که شیوه اجرای این کار را مدیریت می‌کنند. با این حال، یافتن مثال پایداری از شیوه پیاده‌سازی قابلیتی مانند این و همزمان پیروی از اصولی که گوگل برای توسعه اندروید توصیه می‌کند، مانند نوشتن کد در کاتلین با استفاده از الگوی معماری MVVM، استفاده از «اتصال داده» (data binding) و بهره‌گیری از «کامپوننت‌های معماری اندروید» (AAC) مانند ViewModel و Navigation در صورت امکان، کار ساده‌ای محسوب نمی‌شود.

بنابراین بدون هیچ توضیح اضافی در ادامه مثال ساده‌ای از طراحی سطح بالای یک قابلیت لانچر اپلیکیشن را می‌بینید که در ادامه هر یک از کامپوننت‌های شماره‌گذاری شده در نمودار نیز توضیح داده شده‌اند. چند کلاس کمکی مانند ابزار Logger به جهت کاستن از پیچیدگی حذف شده‌اند، اما شما می‌توانید کد کامل پروژه را در این ریپوی گیت‌هاب (+) مشاهده کنید.

لانچر اندروید
برای مشاهده تصویر در اندازه بزرگتر روی آن کلیک کنید.

هدف از هر کامپوننت شماره‌گذاری شده در نمودار فوق در ادامه توضیح داده شده است.

1. LauncherDialog

کامپوننت AppLauncherDialog، کادر محاوره‌ای که برای کاربر نمایش پیدا می‌کند را شامل می‌شود و مجموعه اپلیکیشن‌هایی که می‌توان اجرا کرد را ارائه می‌کند. این کلاس از اتصال‌های داده استفاده می‌کند و AppLauncherViewModel را «مشاهده» (Observe) می‌کند، به طوری که تغییرات داده‌ها بی‌درنگ در UI بازتاب می‌یابند. کلاس این کادر محاوره‌ای در زمینه همه اکشن‌ها (مانند لانچ کردن اپلیکیشن) مختصر است و از سوی «مدل نما» (View Model) مدیریت می‌شود. در نهایت باید اشاره کرد که در پروژه نمونه این کادر محاوره از داشبورد قالب‌بندی باز می‌شود و به منظور نمایش ناوبری از فرگمان به یک کادر محاوره‌ای با کامپوننت معماری Navigation طراحی شده است.

2. AppLauncherViewModel

کلاس AppLauncherViewModel شامل داده‌هایی است که درون نما (AppLauncherDialog) عرضه می‌شوند. داده‌های ارائه شده از طریق واداشتن AppLauncherViewModel برای تماشای مدل داده (AppLauncherModel) به دست آمده‌اند. تا پیش از ارائه، داده‌ها انتقال می‌یابند و هم قابلیت تعامل با اکشن می‌یابند و هم قابلیت عرضه پیدا می‌کنند. در مثال ما این وضعیت شامل استخراج اطلاعات لانچ از هر یک از پکیج‌های اپلیکیشن در مدل داده است و بدین ترتیب از ایجاد نسخه تکراری پکیج‌ها خودداری می‌شود و هر پکیجی که روی دستگاه نباشد حذف می‌شود. در نهایت اپلیکیشن‌ها بر مبنای نام مرتب‌سازی می‌شوند. قطعه کد زیر شیوه استخراج اطلاعات اپلیکیشن از طریق نام پکیج را نمایش می‌دهد.

3. AppLauncherModel

AppLauncherModel شامل داده‌های از پیش تبدیل یافته یا خامی است که از سوی AppLauncherViewModel مورد مشاهده (Obderve) قرار می‌گیرند و در نهایت در AppLauncherDialog ارائه می‌شوند. این داده‌ها صرفاً مجموعه‌ای از نام‌های پکیج محسوب می‌شوند که از سوی لانچر مورد پشتیبانی قرار می‌گیرند. همچنین فلگی وجود دارد که تعیین می‌کند آیا اپلیکیشن باید به صوت نرمال لانچ شود یا باید آن را در حالت «آزاد» (free form) یا «شناور» (floating) لانچ کنیم. این حالت‌ها صرفاً روی دستگاه‌هایی پشتیبانی می‌شوند که اندروید Nougat و بالاتر را اجرا می‌کنند. البته متأسفانه همه دستگاه‌هایی که اندروید Nougat یا بالاتر را اجرا می‌کنند، امکان اجرای این حالت را ندارند.

4. AppLauncherFileMonitor

قابلیت لانچر اپلیکیشن باید بداند کدام اپلیکیشن‌ها برای کاربر نمایش پیدا می‌کنند. این اطلاعات می‌تواند از چندین محل تأمین شود. همه این موارد به شیوه‌ای که می‌خواهید این قابلیت را کنترل کنید وابسته هستند. آیا این قابلیت باید از سوی شما کنترل شود و یا می‌توان کنترل آن را به کاربر سپرد؟ اگر این قابلیت از شما کنترل شود، در این صورت آیا داده‌ها برای نمونه در یک پایگاه داده ابری مانند Firebase Cloud Store موجود هستند یا نه. اگر قرار است از سوی کاربر کنترل شوند، در این صورت آیا اطلاعات در یک پایگاه داده محلی مانند SQLite (با بهره‌گیری از Room) با حتی در بخش Shared Preferences موجود هستند و کاربر می‌تواند اپلیکیشن‌های جدید را در زمان اجرا به این قابلیت اضافه کند یا نه. ما در مثال نسبتاً ساده خودمان ذخیره پیکربندی در قالب یک فایل JSON در دایرکتوری داده اپلیکیشن را انتخاب کردیم.

ما از کلاس سرویس AppLauncherFileMonitor برای نظارت بر فایل پیکربندی که قبلاً اشاره کردیم استفاده می‌کنیم و به این ترتیب زمانی که تغییری ایجاد شود، یک رویداد با استفاده از EventBus تحریک می‌شود. کلاس AppLauncherModel در این رویدادها ثبت نام کرده و مدل داده‌ها را در موارد مقتضی به‌روزرسانی می‌کند و سپس همه این مسیر تا نما (AppLauncherDialog) را طی می‌کند. قطعه کد زیر روش نظارت بر فایل و تحریک رویداد برای گزارش‌گیری که در مثال مربوطه استفاده شده را نمایش می‌دهد:

اگر بخواهیم همه این موارد را کنار هم قرار دهیم، ما اکنون مشغول ساخت یک قابلیت لانچر در اپلیکیشن ابتدایی خود هستیم. تصویر زیر دموی لانچر اپلیکیشن نمونه را در حالت اجرا نمایش می‌دهد.

لانچر اندروید

اساساً همه کار همین است و به این ترتیب این مقاله به پایان می‌رسد.


منبع: فرادرس

قالب‌های گیت هاب برای درخواست‌های Pull یکپارچه و هوشمندانه — راهنمای کاربردی

در این مقاله قصد داریم یک روش بهتر، آسان‌تر و کارآمدتر برای حفظ یکپارچگی در میان درخواست‌های pull گیت‌هاب و حتی سفارشی‌سازی کامل آن‌ها در یک تیم توسعه معرفی کنیم. امکان این کار به کمک قالب های گیت هاب فراهم شده است.

مشکلات رایج

اگر تاکنون به عنوان یک توسعه‌دهنده نرم‌افزار با گیت‌هاب کارکرده باشید، چه این کار را به صورت انفرادی انجام داده باشید و چه بخشی از یک تیم بوده باشید، احتمالاً با مشکلاتی که در ادامه معرفی خواهیم کرد آشنا هستید.

درج پیام‌های کامیت خیلی کوتاه

نخستین مشکل رایج پیام کامیت بی‌معنی است. این پیام‌ها عموماً چندین بار ارسال می‌شوند، چون ما توسعه‌دهندگان تنبلی هستیم و عادت داریم تغییرات مختلف را با پیام‌های یکسانی به گیت‌هاب پوش کنیم و هیچ توجه نداریم که آن تغییرات هیچ ارتباطی با پیام مربوطه ندارند.

قالب های گیت هاب

نکته: اگر می‌خواهید برخی پیام‌های کامیت جالب و سرگرم‌کننده را بخوانید، سری به این وب‌سایت (+) بزنید. با رفرش کردن صفحه پیام‌های کامیت عجیب‌وغریبی را مشاهده خواهید کرد.

درخواست‌های Pull کاملاً معماگونه

احتمالاً یک درخواست pull مانند درخواست زیر را در یک ریپو دیده‌اید (و یا ارسال کرده‌اید). تلاش نکنید انکار کنید! نام و جزییات جهت حفظ حریم خصوصی پنهان شده‌اند:

قالب های گیت هاب

تصویر فوق یک درخواست pull واقعی روی گیت‌هاب است و پیام‌های کامیت فراوانی دارد که معنی بسیار کمی دارند و برای کسی که به آن‌ها نگاه می‌کند جزییات چندانی از آنچه در حال اتفاق افتادن است ارائه نمی‌کنند.

در این مرحله، ممکن است با خود فکر کنید، این کار در واقع شبیه همان کاری است که خودتان قبلاً دیده‌اید (انجام داده‌اید) و مشکل چندانی ایجاد نمی‌کند.

اگر یک توسعه‌دهنده منفرد هستید که روی پروژه شخصی خود کار می‌کنید، همواره از همه چیز در کدبیس اطلاع دارید و شاید حق با شما باشد. اما اگر در حال کار با تیمی متشکل از 10 یا 20 توسعه‌دهنده دیگر هستید و یک اپلیکیشن می‌سازید که از سوی صدها یا هزاران نفر استفاده می‌شود و توسعه‌دهندگان دیگر باید تغییراتی که شما ایجاد کرده‌اید را ببینند و تشخیص دهند که کد منطقی است یا نه و در ادامه هیچ مشکلی در شاخه master کد ایجاد نمی‌کند، در این صورت با یک مشکل بسیار بزرگ مواجه هستیم.

راه‌حل‌ها

برای حل این مشکل‌ها چند راه‌حل مختلف وجود دارند که ممکن است گذشته تجربه کرده باشید.

گزینه 1: فرایند کاملاً دستی

یک مجموعه نسبی از الزامات داریم که باید به هر درخواست pull جدید اضافه کنیم. این موارد چیزهایی مانند این هستند:

  • لینک‌هایی به روند قابلیتی که مشغول کار روی آن هستیم روی backlog.
  • لینک‌هایی به Jenkiks خودکار که شاخه ما بر مبنای آن ساخته شده و درون QA توزیع یافته است.
  • لینک‌هایی به شاخه قابلیت برای تست کردن.
  • گزارش پوشش کد
  • لینک‌های به PR-های مرتبط در ریپوهای دیگر و غیره.

این راه‌حل به درستی کار می‌کند، مگر این که افراد اضافه کردن یکی یا چند مورد از این لینک‌ها را فراموش کنند (یا به آن اهمیت ندهند) و یک سیستم صورت‌بندی برای اعضای جدید که به تیم ملحق می‌شوند نداشته‌ باشیم تا بدانند قالب PR-هایشان باید چگونه باشد.

گزینه 2: قالبی که باید کپی شده و هر بار در PR چسبانده شود

تلاش دوم ما کمی غیر دستی‌تر است. در این روش از یک قالب درخواست pull استفاده می‌شود که تیم می‌تواند در یک فایل txt. در اسلک نگهداری کند و آن را در یکی از کانال‌های تیم توسعه پین کند. ظاهر آن می‌تواند به صورت زیر باشد:

Story

Jira, Pivotal Tracker, (link to where the story you worked on lives)

PR Branch

URL to the automated branch build for feature testing

Code Coverage

URL to the automated code coverage report run during the automated build process

e2e

URL to the automated end to end tests run against the feature branch

UX Approved

yes / no

Newman

yes / no

Related PR’s

TBD

اما در این حالت هم اعضای تیم باید بروند و دنبال قالب بگردند، قالب‌بندی نشانه‌گذاری آن را کپی کنند و آن را درون همه درخواست‌های pull وارد کرده و محتوای آن را نیز پر کنند.

این گزینه بهتری محسوب می‌شود، زیرا دست‌کم قالب تضمین می‌کند که احتمال فراموش کردن موارد مختلف کمتر است، اما همچنان مستعد خطا است. در اغلب موارد افراد تیم به سراغ PR-های قدیمی می‌روند، آن را باز می‌کنند و محتوای آن را کپی کرده و در PR جدید می‌چسبانند. البته این کار اشکالی ندارد، اما گاهی افراد فراموش می‌کنند که لینک استوری را به‌روز کنند یا لینک سربه‌سر تست را درج نمی‌کنند و مشکل پیش می‌آید.

این بهترین راه‌حلی بود که اعضای تیم می‌توانستند داشته باشند تا این که برخی توسعه‌دهندگان تازه‌کار به تیم ما اضافه شدند. زمانی که راه‌حل بهینه خود یعنی قالب PR را که استفاده می‌کردیم به اعضای جدید تیم نشان دادیم، آن‌ها پرسیدند که چرا از قالب‌های گیت‌هاب استفاده نمی‌کنیم؟ پاسخ ما این بود که: «چون هیچ یک از ما تاکنون چیزی در مورد آن نشنیده‌ایم!» در نهایت بدین ترتیب با این مفهوم به شدت جالب و البته آسان آشنا شدیم.

گزینه 3: قالب‌های گیت‌ هاب؛ روش خودکار برای انجام درخواست Pull

این گزینه راه‌حل بهتری محسوب می‌شود و در واقع راه‌حلی است که هرکس باید استفاده کند. قالب‌های گیت‌هاب ابداع بسیار جالبی از سوی گیت‌هاب برای درخواست‌های pull و همچنین issue-ها محسوب می‌شوند. از آنجا که PR-ها دغدغه هر تیم توسعه‌ای هستند، به مستندات گیت‌هاب مراجعه کردیم تا در مورد آن بیشتر بدانیم:

زمانی که یک قالب درخواست pull به ریپازیتوری خود اضافه کنید، مشارکت‌کنندگان در پروژه به صورت خودکار محتوای قالب را در بدنه درخواست pull خود مشاهده خواهند کرد.

این همان چیزی است که هر تیم توسعه‌ای نیاز دارد. این روش تضمین می‌کند که همه درخواست‌های pull یک شکل هستند و لازم نیست اعضای تیم در مورد آن نگرانی داشته باشند. راه‌اندازی آن نیز بسیار آسان است.

راه‌اندازی قالب‌های گیت‌ هاب در یک پروژه

با این که راه‌اندازی یک قالب PR بسیار سرراست است، اما برای سهولت هر چه بیشتر در ادامه مراحل آن را توضیح داده‌ایم.

گام 1: افزودن یک پوشه /github. به ریشه پروژه

مستندات این بخش بیان می‌کنند که می‌توان یک قالب PR را در خود ریشه دایرکتوری پروژه یا در پوشه‌ای به نام /docs یا /github. اضافه کرد. از آنجا که ما می‌خواهیم پوشه PR را در اغلب موارد که مشغول توسعه هستیم نادیده بگیریم. تصمیم گرفتیم پوشه قالب را درون پوشه /github. بسازیم:

قالب های گیت هاب

گام 2: افزودن فایل نشانه‌گذاری pull_request_template.md درون پوشه

برای این که گیت‌هاب به صورت خودکار قالب‌بندی را از فایل نشانه‌گذاری برای قالب‌های PR بردارد، باید نام فایل را pull_request_template.md بگذارید. در ادامه نمونه‌ای از قالبی که تیم ما استفاده می‌کند را می‌بینید:

Tracker ID: #ADD LINK TO PIVOTAL STORY

Unit tests completed?: (Y/N)

PR Branch #ADD LINK TO PR BRANCH

Code Coverage & Build Info #ADD LINK TO JENKINS CONSOLE

E2E Approved #ADD LINK TO PASSING E2E TESTS

Windows Testing #HAS WINDOWS BEEN TESTED?

Related PR #ADD ANY RELATED PULL REQUESTS

UX Approved #ADD UX APPROVAL IF NEEDED

گام 3: افزودن این فایل‌ها به شاخه master و آماده‌سازی همه درخواست‌های PR

در ادامه تصویری از یک ریپازیتوری را می‌بینید که قالب درخواست PR به آن اضافه شده و یک درخواست pull نیز به عنوان نمونه ایجاد شده است:

قالب های گیت هاب

افزودن یک قالب PR استاندارد به هر ریپازیتوری دقیقاً به همین سادگی است.

سخن پایانی

زمانی که با تیمی از توسعه‌دهندگان سر و کار داریم که روی محصولی حساس مشغول کار هستند، درخواست‌های pull یک ضرورت حیاتی محسوب می‌شوند، اما به کمک امکان pull_request_templates در گیت‌هاب این مشکل می‌تواند به آسانی مرتفع شود.

مزیت‌های قالب‌های صورت‌بندی شده PR زیاد هستند، اما برخی از آن‌ها که می‌توان اشاره کرد میزان انعطاف‌پذیری آن‌ها است. مهم نیست که تیم شما چه چیزی را مورد بررسی و پذیرش قرار می‌دهد، یک درخواست Pull را می‌توان به سادگی در هر ریپازیتوری جای داده و پیاده‌سازی کرد. این همان نوع از سهولت کار توسعه‌دهنده‌ها است که برای صورت‌بندی موارد ضروری مانند PR-ها به آن نیاز داریم.

منبع: فرادرس

یادگیری ماشین در اندروید با ML Kit برای فایربیس — از صفر تا صد

آن روزها که استفاده از قابلیت‌های یادگیری ماشین تنها روی کلود امکان داشت و این مسئله نیازمند توان محاسباتی بالا، سخت‌افزار پیشرفته و مواردی از این دست بود، گذشته است. امروزه دستگاه‌های موبایل بسیار قدرتمندتر شده‌اند و الگوریتم‌های ما نیز کارایی بالاتری یافته‌اند. همه این موارد منجر به این نتیجه شده است که بهره‌گیری از یادگیری ماشین روی دستگاه‌های همراه امکان یافته است و دیگر صرفاً یک نظریه عملی-تخیلی محسوب نمی‌شود. در این نوشته به بررسی عملی یک پروژه یادگیری ماشین در اندروید با استفاده از  ML Kit‌ برای «فایربیس» (Firebase) می‌پردازیم.

مقدمه

یادگیری ماشین امروزه روی دستگاه‌های گوناگون در همه جا استفاده می‌شود. نمونه‌هایی از این مسئله به شرح زیر هستند:

  • دستیار هوشمند: کورتانا، Siri و دستیار گوگل نمونه‌هایی از این دستیارها هستند. دستیار گوگل در کنفرانس IO امسال یک به‌روزرسانی عمده دریافت کرده است و عمده توجه بر روی افزایش ظرفیت‌های یادگیری ماشین آن روی دستگاه‌های همراه بوده است.
  • فیلترهای اسنپ‌چت: اسنپ‌چت از یادگیری ماشین برای تشخیص چهره‌های انسان و قرار دادن فیلترهای 3 بعدی مانند عینک، کلاه، و.. روی آن‌ها استفاده می‌کند.
  • پاسخ هوشمند: پاسخ هوشمند (Smart Replay) یک از قابلیت‌های بسیاری از دستگاه‌های امروزی است و در اپلیکیشن‌های چت مانند واتس اپ و غیره استفاده می‌شود؛ هدف آن ارائه پیغام‌های از پیش تعریف شده‌ای است که می‌توانید از آن‌ها را در پاسخ به پیام‌های دریافتی گوناگون خود بهره بگیرید.
  • گوگل لنز: با این که این سرویس همچنان در مراحل ابتدایی خود است، اما از مدل‌های یادگیری ماشین برای شناسایی و برچسب‌گذاری اشیا استفاده می‌کند.

این فهرست را می‌توان بسیار ادامه داد، بنابراین به این مقدار اکتفا می‌کنیم. در این مقاله به بررسی شیوه‌های استفاده از یادگیری ماشین در اپلیکیشن‌های اندروید و هوشمندتر ساختن آن‌ها می‌پردازیم.

ML Kit برای فایربیس چیست؟

ML Kit‌ برای فایربیس یک SDK موبایل است که گنجاندن ظرفیت‌های یادگیری ماشین در اپلیکیشن‌ها را برای توسعه‌دهندگان موبایل آسان‌تر ساخته است. این کیت شامل API های پیش‌ساخته زیر است:

  • بازشناسی متن: این API برای بازشناسی و استخراج متن از تصاویر استفاده می‌شود.
  • تشخیص چهره: برای تشخیص چهره و نشانه‌گذاری بخش‌های مختلف چهره همراه با مرزهای آن استفاده می‌شود.
  • تشخیص و ردگیری شیء: برای تشخیص، ردگیری و طبقه‌بندی اشیا در تصاویر دوربین و عکس‌های از پیش ثبت شده استفاده می‌شود.
  • برچسب‌گذاری تصویر: شناسایی اشیا، مکان‌ها، فعالیت‌ها، گونه‌های حیوانات و مواردی از این دست.
  • اسکن بارکد: برای اسکن و پردازش بارکدها استفاده می‌شود.
  • تشخیص مکان: برای شناسایی امکان معروف و شناخته شده در تصاویر به کار می‌رود.
  • شناسه زبان: برای تشخیص زبان یک متن استفاده می‌شود.
  • ترجمه روی دستگاه: متن نمایش داده شده روی یک دستگاه را از یک زبان به زبانی دیگر ترجمه می‌کند.
  • پاسخ هوشمند: پاسخ‌های متنی هوشمندانه‌ای بر مبنای پیام‌های قبلی تولید می‌کند.

ML Kit‌ در اصل و به زبان ساده، راهکاری است که با آن می‌توانید از پیچیدگی‌ها بهره‌گیری از یادگیری ماشین در اپلیکیشن‌های خود برای تلفن‌های هوشمند بکاهید.

چه چیزی خواهیم ساخت؟

ما در این مقاله از ظرفیت تشخیص چهره ML Kit‌ برای شناسایی چهره‌ها در یک تصویر استفاده می‌کنیم. بدین منظور تصاویر را از طریق دوربین می‌گیریم و اینترفیس را روی آن اجرا می‌کنیم.

برای استفاده از ML Kit‌ لازم است که یک حساب «فایربیس» (Firebase) داشته باشید. اگر چنین حسابی ندارید به این صفحه (+) مراجعه کنید و یک حساب باز کنید. توجه داشته باشید در زمان نگارش مقاله، این حساب برای دسترسی ایرانیان مسدود بوده است و برای دسترسی به آن نیاز به استفاده از پراکسی وجود دارد.

ایجاد یک پروژه روی فایربیس

زمانی که یک حساب کاربری روی فایربیس باز کردید، به این صفحه (+) بروید و روی add project کلیک کنید.

کیت ML

یک نام به پروژه خود بدهید و روی Create کلیک کنید. ممکن است چند ثانیه طول بکشد تا پروژه شما آماده شود. سپس در ادامه یک اپلیکیشن اندروید به پروژه خود اضافه می‌کنیم.

افزودن اپلیکیشن

اندروید استودیو را باز کنید و یک پروژه جدید با یک اکتیویتی خالی اضافه کنید. سپس روی Tools -> Firebase کلیک کنید. بدین ترتیب دستیار فایربیس در پنل سمت راست باز می‌شود. در فهرست گزینه‌ها، ML Kit را انتخاب کرده و روی گزینه زیر کلیک کنید:

Use ML Kit to detect faces in images and video

کیت ML

احتمالاً از شما درخواست می‌شود که اندروید استودیو خود را به یک اپلیکیشن در فایربیس وصل کنید. روی connect کلیک کرده و در پروژه فایربیس خود sign in کنید. زمانی که اندروید استودیو اجازه دسترسی به پروژه فایربیس را داد، از شما خواسته می‌شود که یک پروژه انتخاب کنید. پروژه‌ای را که در گام قبلی ایجاد کردید انتخاب کنید. پس از آن باید ML Kit را به اپلیکیشن خود اضافه کنید که یک فرایند تک کلیکی محسوب می‌شود. زمانی که کار اضافه کردن وابستگی‌ها به پایان رسید، آماده هستید که اپلیکیشن خود را ایجاد کنید.

پیکربندی AndroidManifest.xml

شما باید فایل AndroidManifest.xml خود را طوری ویرایش کنید که امکان یادگیری ماشین آفلاین در اپلیکیشن اندروید خود را داشته باشید. متا-تگ زیر را درست زیر تگ اپلیکیشن خود وارد کنید.

ضمناً قصد داریم یک قابلیت دوربین به اپلیکیشن خود اضافه کنیم و از این رو یک تگ permission در مانیفست اضافه می‌کنیم.

افزودن یک دوربین به اپلیکیشن

برای افزودن دوربین به اپلیکیشن باید CameraKit مربوط به WonderKiln را اضافه کنیم. برای استفاده از CameraKit وابستگی‌های زیر را در فایل build.gradle سطح اپلیکیشن اضافه می‌کنیم.

نکته: اگر می‌خواهید از کاتلین در اپلیکیشن اندروید خود استفاده کنید، باید وابستگی‌های کاتلین نیز در آن گنجانده شوند.

پروژه را Sync کنید تا وابستگی‌ها دانلود شوند. سپس یک CameraKitView در فایل لی‌آوت activity_main.xml اضافه می‌کنیم. به این منظور کد زیر را در فایل لی‌‌آوت اضافه کنید.

همچنین یک دکمه به زیر صفحه اضافه می‌کنیم تا به وسیله آن عکس بگیریم.

پیکربندی دوربین

ما دوربین را در فایل MainActivity.kt مقداردهی می‌کنیم. به این منظور برخی متدهای چرخه عمر را override خواهیم کرد و درون این متدها، متدهای چرخه عمر را برای دوربین فراخوانی می‌کنیم:

از اندروید نسخه M به بعد باید مجوزهای زمان اجرا را نیز تقاضا کنیم. این وضعیت از سوی خود کتابخانه CameraKit مربوط به WonderKiln مدیریت می‌شود. به این منظور کافی است متد زیر را override کنیم:

عکس گرفتن

ما با فشردن دکمه یک عکس می‌گیریم و آن را به دتکتور ارسال می‌کنیم. سپس یک مدل یادگیری ماشین روی تصویر اعمال می‌کنیم تا چهره را شناسایی کنیم. یک onclicklistener روی دکمه و درون تابع onClick تنظیم می‌کنیم و درون آن تابع captureImage مربوط به ویوی Camera را فراخوانی می‌کنیم.

در ادامه مقدار byteArray دریافتی را در یک bitmap دیکود می‌کنیم و سپس bitmap را به یک اندازه معقول مقیاس‌بندی می‌نماییم. این اندازه اساساً باید به اندازه camera_view ما باشد.

اکنون زمان آن فرا رسیده است که دتکتور ما روی bitmap اجرا شود. به این منظور تابع جداگانه runDetector() اجرا می‌کنیم.

شروع تشخیص

این بخش اصلی اپلیکیشن ما است. ما یک دتکتور روی تصویر ورودی اجرا می‌کنیم. ابتدا یک FirebaseVisionImage از bitmap دریافتی تهیه می‌کنیم.

سپس نوبت آن می‌رسد که برخی گزینه‌ها مانند performanceMode ،countourMode ،landmarkMode و غیره را اضافه کنیم.

اکنون دتکتور خود را با استفاده از گزینه‌ها می‌سازیم.

در نهایت، نوبت آن می‌رسد که فرایند شناسایی را شروع کنیم. ما با استفاده از این دتکتور تابع detectInImage را فراخوانی کرده و تصویر را به آن ارسال می‌کنیم. در ادامه callback-هایی برای موفقیت یا شکست دریافت می‌کنیم.

طرز کار تابع runDetector به صورت زیر است:

ایجاد کادر پیرامونی

برای ایجاد یک کادر پیرامونی که چهره‌های شناسایی‌شده را احاطه کند، باید دو view ایجاد کنیم. ویوی اول یک ویوی رویی شفاف است که روی آن کادر را رسم می‌کنیم. سپس کادر واقعی را روی تصویر قرار می‌دهیم.

فایل GraphicOverlay.java به صورت زیر است:

کادر مستطیلی واقعی به صورت زیر است:

اینک زمان آن رسیده که این ویوی graphicOverlay را در فایل لی‌آوت activity_main.xml خود اضافه کنیم.

در نهایت فایل activity_main.xml به صورت زیر درمی‌آید:

اینک زمان آن است که کادر پیرامونی چهره‌های شناسایی‌ شده را رسم کنیم. ما با استفاده از دتکتور چهره‌ها را در تابع ()processFaceResult دریافت کرده‌ایم.

در ادامه حلقه‌ای روی همه چهره‌ها تعریف کرده و یک کادر روی هر چهره به صورت زیر اضافه می‌کنیم:

سخن پایانی

در نهایت اپلیکیشن ما چیزی مانند تصویر زیر خواهد بود:

شما می‌توانید سورس کد این اپلیکیشن را از این صفحه گیت‌هاب (+) دانلود کنید. روی این کد کار کنید و ایده‌های مختلفی که برای پیاده‌سازی یادگیری ماشین در اپلیکیشن‌های اندرویدی دارید امتحان نمایید.


منبع: فرادرس